├── .commitlintrc.js ├── .editorconfig ├── .env.example ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── Bug_Report.md │ └── Feature_Request.md ├── labeler.yaml ├── labels.yaml ├── renovate.json5 ├── renovate.json5.orig ├── renovate │ ├── autoMerge.json5 │ ├── commitMessage.json5 │ ├── groups.json5 │ ├── labels.json5 │ └── semanticCommits.json5 └── workflows │ ├── ci.yml │ ├── link-check.yaml │ ├── meta-labeler.yaml │ └── meta-sync-labels.yaml ├── .gitignore ├── .husky ├── commit-msg ├── pre-commit └── prepare-commit-msg ├── .lintstagedrc.js ├── .prettierignore ├── .prettierrc ├── .vscode └── extensions.json ├── .vuepress └── config.ts ├── .yarn └── releases │ └── yarn-1.22.19.cjs ├── .yarnrc ├── LICENSE ├── README.md ├── TODO.md ├── apps ├── .gitkeep ├── admin-e2e │ ├── .eslintrc.json │ ├── cypress.config.ts │ ├── project.json │ ├── src │ │ ├── e2e │ │ │ └── app.cy.ts │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── app.po.ts │ │ │ ├── commands.ts │ │ │ └── e2e.ts │ └── tsconfig.json ├── admin │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── module-federation.config.js │ ├── project.json │ ├── proxy.conf.json │ ├── src │ │ ├── app │ │ │ ├── app.config.ts │ │ │ ├── app.routes.ts │ │ │ └── remote-entry │ │ │ │ ├── entry.component.ts │ │ │ │ ├── entry.routes.ts │ │ │ │ └── nx-welcome.component.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── bootstrap.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── styles.css │ │ └── test-setup.ts │ ├── tailwind.config.js │ ├── tsconfig.app.json │ ├── tsconfig.editor.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── webpack.config.js │ └── webpack.prod.config.js ├── client-e2e │ ├── .eslintrc.json │ ├── cypress.config.ts │ ├── project.json │ ├── src │ │ ├── e2e │ │ │ └── app.cy.ts │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── app.po.ts │ │ │ ├── commands.ts │ │ │ └── e2e.ts │ └── tsconfig.json ├── client │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── project.json │ ├── proxy.conf.json │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.config.ts │ │ │ ├── app.routes.ts │ │ │ └── nx-welcome.component.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── styles.scss │ │ └── test-setup.ts │ ├── tailwind.config.js │ ├── tsconfig.app.json │ ├── tsconfig.editor.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── login-e2e │ ├── .eslintrc.json │ ├── cypress.config.ts │ ├── project.json │ ├── src │ │ ├── e2e │ │ │ └── app.cy.ts │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── app.po.ts │ │ │ ├── commands.ts │ │ │ └── e2e.ts │ └── tsconfig.json ├── login │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── module-federation.config.js │ ├── project.json │ ├── proxy.conf.json │ ├── src │ │ ├── app │ │ │ ├── app.config.ts │ │ │ ├── app.routes.ts │ │ │ └── remote-entry │ │ │ │ ├── entry.component.html │ │ │ │ ├── entry.component.ts │ │ │ │ └── entry.routes.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── bootstrap.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── styles.css │ │ └── test-setup.ts │ ├── tailwind.config.js │ ├── tsconfig.app.json │ ├── tsconfig.editor.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── webpack.config.js │ └── webpack.prod.config.js ├── server-e2e │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── server │ │ │ └── server.spec.ts │ │ └── support │ │ │ ├── global-setup.ts │ │ │ ├── global-teardown.ts │ │ │ └── test-setup.ts │ ├── tsconfig.json │ └── tsconfig.spec.json ├── server │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── app │ │ │ └── app.module.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ └── templates │ │ │ │ ├── contexts │ │ │ │ ├── index.ts │ │ │ │ ├── layout.email-context.ts │ │ │ │ ├── reset-password.email-context.ts │ │ │ │ └── verify-email.email-context.ts │ │ │ │ ├── partials │ │ │ │ ├── footer.hbs │ │ │ │ ├── header.hbs │ │ │ │ ├── layout.hbs │ │ │ │ └── style.hbs │ │ │ │ ├── reset-password.hbs │ │ │ │ └── verify-email.hbs │ │ ├── main.ts │ │ └── swagger.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── webpack.config.js ├── shell-e2e │ ├── .eslintrc.json │ ├── cypress.config.ts │ ├── project.json │ ├── src │ │ ├── e2e │ │ │ └── app.cy.ts │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── app.po.ts │ │ │ ├── commands.ts │ │ │ └── e2e.ts │ └── tsconfig.json └── shell │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── module-federation.config.js │ ├── project.json │ ├── proxy.conf.json │ ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.config.ts │ │ ├── app.routes.ts │ │ └── nx-welcome.component.ts │ ├── assets │ │ ├── .gitkeep │ │ └── module-federation.manifest.json │ ├── bootstrap.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── remotes.d.ts │ ├── styles.css │ └── test-setup.ts │ ├── tailwind.config.js │ ├── tsconfig.app.json │ ├── tsconfig.editor.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── webpack.config.js ├── assets └── logo.svg ├── docker-compose.yml ├── docs ├── ARCHITECTURE.md ├── FRONTEND.md └── GETTING_STARTED.md ├── jest.config.ts ├── jest.preset.js ├── libs ├── .gitkeep ├── frontend │ ├── data-access-common │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── constants.ts │ │ │ │ ├── guards │ │ │ │ │ ├── auth-guard.guard.spec.ts │ │ │ │ │ └── auth-guard.guard.ts │ │ │ │ └── interceptors │ │ │ │ │ ├── jwt.interceptor.spec.ts │ │ │ │ │ └── jwt.interceptor.ts │ │ │ └── test-setup.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── data-access-user │ │ ├── .eslintrc.json │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── constants.ts │ │ │ │ ├── user-state.ts │ │ │ │ └── user.service.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── feature-admin-users │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── frontend-feature-admin-users │ │ │ │ │ ├── frontend-feature-admin-users.component.css │ │ │ │ │ ├── frontend-feature-admin-users.component.html │ │ │ │ │ ├── frontend-feature-admin-users.component.spec.ts │ │ │ │ │ └── frontend-feature-admin-users.component.ts │ │ │ │ ├── lib.routes.ts │ │ │ │ └── user-upsert-dialog │ │ │ │ │ ├── user-upsert-dialog.component.html │ │ │ │ │ ├── user-upsert-dialog.component.scss │ │ │ │ │ └── user-upsert-dialog.component.ts │ │ │ └── test-setup.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── feature-login │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── frontend-feature-login │ │ │ │ │ ├── frontend-feature-login.component.html │ │ │ │ │ ├── frontend-feature-login.component.scss │ │ │ │ │ ├── frontend-feature-login.component.spec.ts │ │ │ │ │ └── frontend-feature-login.component.ts │ │ │ │ └── lib.routes.ts │ │ │ └── test-setup.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ └── util-common │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ ├── index.ts │ │ ├── lib │ │ │ └── directives │ │ │ │ ├── index.ts │ │ │ │ └── upsert-dialog-directive │ │ │ │ ├── upsert-dialog-data.interface.ts │ │ │ │ └── upsert-dialog.directive.ts │ │ └── test-setup.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json ├── server │ ├── data-access │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── api-page-ok-response.decorator.ts │ │ │ │ ├── database │ │ │ │ ├── database.module.ts │ │ │ │ ├── entities │ │ │ │ │ └── user.orm-entity.ts │ │ │ │ ├── pagination.service.ts │ │ │ │ ├── seeds │ │ │ │ │ ├── seed-database.ts │ │ │ │ │ ├── seed.module.ts │ │ │ │ │ └── users.database-seed.ts │ │ │ │ └── subscribers │ │ │ │ │ └── user.entity-subscriber.ts │ │ │ │ ├── dtos │ │ │ │ ├── auth │ │ │ │ │ ├── forgot-password.dto.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── reset-password.dto.ts │ │ │ │ │ └── token-response.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pagination │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── pagination-meta.dto.ts │ │ │ │ │ ├── pagination-options.dto.ts │ │ │ │ │ └── pagination-response.dto.ts │ │ │ │ └── users │ │ │ │ │ ├── create-user.dto.ts │ │ │ │ │ ├── login-user.dto.ts │ │ │ │ │ ├── update-user.dto.ts │ │ │ │ │ └── user.dto.ts │ │ │ │ └── server-data-access.module.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── feat-auth │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── google-auth │ │ │ │ ├── google-auth.controller.spec.ts │ │ │ │ ├── google-auth.controller.ts │ │ │ │ ├── google-auth.service.ts │ │ │ │ └── google-login.dto.ts │ │ │ │ ├── server-feat-auth.controller.spec.ts │ │ │ │ ├── server-feat-auth.controller.ts │ │ │ │ ├── server-feat-auth.module.ts │ │ │ │ ├── server-feat-auth.service.spec.ts │ │ │ │ ├── server-feat-auth.service.ts │ │ │ │ └── strategies │ │ │ │ ├── jwt-access.strategy.ts │ │ │ │ └── jwt-refresh.strategy.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── feat-health │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── server-feature-health.controller.spec.ts │ │ │ │ ├── server-feature-health.controller.ts │ │ │ │ └── server-feature-health.module.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── feat-user │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── server-feat-user.controller.spec.ts │ │ │ │ ├── server-feat-user.controller.ts │ │ │ │ ├── server-feat-user.module.ts │ │ │ │ ├── server-feat-user.service.spec.ts │ │ │ │ └── server-feat-user.service.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ ├── util-common │ │ ├── .eslintrc.json │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ ├── config │ │ │ │ ├── app.config.ts │ │ │ │ ├── database.config.ts │ │ │ │ ├── google.config.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mailer.config.ts │ │ │ │ ├── schema.ts │ │ │ │ ├── typeorm-config.service.spec.ts │ │ │ │ └── typeorm-config.service.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── database │ │ │ │ ├── base.orm-entity.ts │ │ │ │ └── index.ts │ │ │ │ ├── decorators │ │ │ │ ├── auth.decorator.ts │ │ │ │ ├── index.ts │ │ │ │ ├── req-user.decorator.ts │ │ │ │ ├── skip-auth.decorator.ts │ │ │ │ ├── use-dto.decorator.ts │ │ │ │ ├── utc-date.decorator.ts │ │ │ │ └── uuid-param.decorator.ts │ │ │ │ ├── dto │ │ │ │ ├── base.dto.ts │ │ │ │ └── index.ts │ │ │ │ ├── exceptions │ │ │ │ ├── index.ts │ │ │ │ ├── token-expired.exception.ts │ │ │ │ └── user-not-found.exception.ts │ │ │ │ ├── filters │ │ │ │ ├── index.ts │ │ │ │ └── query-failed.filter.ts │ │ │ │ ├── guards │ │ │ │ ├── index.ts │ │ │ │ ├── jwt-access.guard.ts │ │ │ │ ├── jwt-refresh.guard.ts │ │ │ │ └── role.guard.ts │ │ │ │ ├── interceptors │ │ │ │ ├── index.ts │ │ │ │ └── logging.interceptor.ts │ │ │ │ ├── loggers │ │ │ │ ├── database.logger.ts │ │ │ │ └── index.ts │ │ │ │ └── types.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ ├── util-mailer │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── mailer-config-options.interface.ts │ │ │ │ ├── server-util-mailer.module.ts │ │ │ │ ├── server-util-mailer.service.spec.ts │ │ │ │ └── server-util-mailer.service.ts │ │ │ ├── templates │ │ │ │ ├── contexts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── layout.email-context.ts │ │ │ │ │ ├── reset-password.email-context.ts │ │ │ │ │ └── verify-email.email-context.ts │ │ │ │ ├── partials │ │ │ │ │ ├── footer.hbs │ │ │ │ │ ├── header.hbs │ │ │ │ │ ├── layout.hbs │ │ │ │ │ └── style.hbs │ │ │ │ ├── reset-password.hbs │ │ │ │ └── verify-email.hbs │ │ │ └── testing │ │ │ │ ├── index.ts │ │ │ │ └── mock-mailer.factory.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ └── tsconfig.spec.json │ └── util-testing │ │ ├── .eslintrc.json │ │ ├── project.json │ │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── mock-repo.factory.ts │ │ │ └── server-util-testing.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json └── shared │ ├── ui-tailwind │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ └── tailwind.config.js │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json │ ├── util-constants │ ├── .eslintrc.json │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── env.constants.ts │ │ │ ├── pagination.contants.ts │ │ │ └── password.constants.ts │ ├── tsconfig.json │ └── tsconfig.lib.json │ └── util-types │ ├── .eslintrc.json │ ├── project.json │ ├── src │ ├── index.ts │ └── lib │ │ ├── enums │ │ ├── auth-provider.enum.ts │ │ ├── index.ts │ │ ├── role.enum.ts │ │ ├── sort-order.enum.ts │ │ └── token-type.enum.ts │ │ ├── interfaces │ │ ├── auth │ │ │ ├── forgot-password.interface.ts │ │ │ ├── google-login.interface.ts │ │ │ ├── index.ts │ │ │ ├── reset-password.interface.ts │ │ │ └── social-payload.interface.ts │ │ ├── base-entity.interface.ts │ │ ├── index.ts │ │ ├── jwt-payload.interface.ts │ │ ├── pagination │ │ │ ├── index.ts │ │ │ ├── paginated-response.interface.ts │ │ │ ├── pagination-meta.interface.ts │ │ │ └── pagination-options.interface.ts │ │ ├── request-user.interface.ts │ │ ├── token-response.interface.ts │ │ ├── user-login.interface.ts │ │ └── user.interface.ts │ │ └── types.ts │ ├── tsconfig.json │ └── tsconfig.lib.json ├── migrations.json ├── nx-cloud.env.example ├── nx.json ├── package.json ├── tools └── tsconfig.tools.json ├── tsconfig.base.json └── yarn.lock /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | const { 2 | utils: { getProjects }, 3 | } = require('@commitlint/config-nx-scopes'); 4 | 5 | module.exports = { 6 | extends: ['@commitlint/config-conventional'], 7 | rules: { 8 | 'scope-enum': async (ctx) => [ 9 | 2, 10 | 'always', 11 | [ 12 | ...(await getProjects( 13 | ctx, 14 | ({ name, projectType }) => !name.includes('e2e'), 15 | )), 16 | 'deploy', 17 | 'repo', 18 | 'docs', 19 | 'deps', 20 | ], 21 | ], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | ENVIRONMENT=development 3 | 4 | #== Server Settings 5 | API_VERSION=v1 6 | API_PREFIX=api 7 | CORS_ORIGINS=* 8 | ENABLE_SWAGGER=true 9 | GENERATE_SWAGGER_JSON=true 10 | JWT_ACCESS_EXPIRATION_TIME=3600s 11 | JWT_ACCESS_EXPIRATION_TIME=86400s 12 | JWT_ACCESS_SECRET=something-something-darkside 13 | JWT_REFRESH_SECRET=foobar 14 | SERVER_HOST=127.0.0.1 15 | SERVER_PORT=3000 16 | SWAGGER_JSON_FILE=swagger.json 17 | 18 | DATABASE_TYPE=postgres 19 | DATABASE_HOST=127.0.0.1 20 | DATABASE_PORT=5432 21 | DATABASE_USERNAME=server 22 | DATABASE_PASSWORD=server 23 | DATABASE_NAME=boilerplate 24 | DATABASE_LOGGING_ENABLED=true 25 | DATABASE_SYNCHRONIZE=true 26 | 27 | #===== Sentry Config 28 | SENTRY_DSN= 29 | 30 | #===== Google Login Integration 31 | SOCIAL_GOOGLE_ENABLED=false 32 | SOCIAL_GOOGLE_CLIENT_ID= 33 | SOCIAL_GOOGLE_CLIENT_SECRET= 34 | 35 | #===== Server SMTP Settings 36 | EMAIL_ENABLED=false 37 | EMAIL_HOST= 38 | EMAIL_PORT=2525 39 | EMAIL_USER="" 40 | EMAIL_PASSWORD="" 41 | EMAIL_TEMPLATE_DIR='/templates/emails/' 42 | EMAIL_IGNORE_TLS=false 43 | EMAIL_REQUIRE_TLS=true 44 | EMAIL_DEBUG=false 45 | EMAIL_DEFAULT_NAME= 46 | EMAIL_DEFAULT_EMAIL= 47 | EMAIL_SECURE=true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["**/*"], 4 | "plugins": ["@nx"], 5 | "overrides": [ 6 | { 7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 8 | "rules": { 9 | "@nx/enforce-module-boundaries": [ 10 | "error", 11 | { 12 | "enforceBuildableLibDependency": true, 13 | "allow": [], 14 | "depConstraints": [ 15 | { 16 | "sourceTag": "*", 17 | "onlyDependOnLibsWithTags": ["*"] 18 | } 19 | ] 20 | } 21 | ] 22 | } 23 | }, 24 | { 25 | "files": ["*.ts", "*.tsx"], 26 | "extends": ["plugin:@nx/typescript"], 27 | "rules": {} 28 | }, 29 | { 30 | "files": ["*.js", "*.jsx"], 31 | "extends": ["plugin:@nx/javascript"], 32 | "rules": {} 33 | }, 34 | { 35 | "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], 36 | "env": { 37 | "jest": true 38 | }, 39 | "rules": {} 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_Request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: "I have a suggestion (and may want to implement it \U0001F642)!" 4 | title: '' 5 | labels: 'i: enhancement, i: needs triage' 6 | assignees: '' 7 | --- 8 | 9 | ## Feature Request 10 | 11 | **Is your feature request related to a problem? Please describe.** 12 | A clear and concise description of what the problem is. Ex. I have an issue when [...] 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. Add any considered drawbacks. 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Teachability, Documentation, Adoption, Migration Strategy** 21 | If you can, explain how users will be able to use this and possibly write out a version the docs. 22 | Maybe a screenshot or design? 23 | -------------------------------------------------------------------------------- /.github/labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repo/apps: 3 | - 'apps/**/*' 4 | repo/libs: 5 | - 'libs/**/*' 6 | # repo/deploy: 7 | # - 'k8s/**/*' 8 | scope/server: 9 | - apps/server/**/* 10 | - apps/server-e2e/**/* 11 | - libs/server/**/* 12 | scope/client: 13 | - apps/client/**/* 14 | - apps/client-e2e/**/* 15 | - libs/client/**/* 16 | scope/shared: 17 | - libs/shared/**/* 18 | type/feature: 19 | - libs/**/feature-*/* 20 | type/data-access: 21 | - libs/**/data-access-*/* 22 | type/util: 23 | - libs/**/util*/* 24 | type/domain: 25 | - libs/**/domain*/* 26 | type/ui: 27 | - libs/**/ui-*/* 28 | dep-updates: 29 | - package.json 30 | - package-lock.json 31 | - yarn.lock 32 | - .nvmrc 33 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | extends: [ 4 | 'config:recommended', 5 | 'group:monorepos', 6 | ':semanticCommits', 7 | ':enablePreCommit', 8 | 'helpers:pinGitHubActionDigests', 9 | ':disableRateLimiting', 10 | 'monorepo:nrwl', 11 | 'monorepo:nest', 12 | 'monorepo:angular', 13 | 'monorepo:angular-cli', 14 | 'monorepo:angular-eslint', 15 | 'monorepo:jest', 16 | ], 17 | schedule: ['every saturday'], 18 | assignees: ['wgd3'], 19 | dependencyDashboard: true, 20 | dependencyDashboardTitle: 'Renovate Dashboard 🤖', 21 | packageRules: [ 22 | { 23 | description: 'Automatically merge minor and patch-level updates', 24 | matchUpdateTypes: ['minor', 'patch', 'digest'], 25 | automerge: true, 26 | automergeType: 'branch', 27 | }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /.github/renovate/autoMerge.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | packageRules: [ 4 | { 5 | description: 'Auto merge GitHub Actions', 6 | matchManagers: ['github-actions'], 7 | automerge: true, 8 | automergeType: 'branch', 9 | ignoreTests: true, 10 | matchUpdateTypes: ['minor', 'patch', 'digest'], 11 | }, 12 | { 13 | description: 'Auto merge container digests', 14 | matchDatasources: ['docker'], 15 | automerge: true, 16 | automergeType: 'branch', 17 | ignoreTests: true, 18 | matchUpdateTypes: ['digest'], 19 | }, 20 | ], 21 | } 22 | -------------------------------------------------------------------------------- /.github/renovate/commitMessage.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | commitMessageTopic: '{{depName}}', 4 | commitMessageExtra: 'to {{newVersion}}', 5 | commitMessageSuffix: '', 6 | packageRules: [], 7 | } 8 | -------------------------------------------------------------------------------- /.github/renovate/groups.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | packageRules: [], 4 | } 5 | -------------------------------------------------------------------------------- /.github/renovate/labels.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | packageRules: [ 4 | { 5 | matchUpdateTypes: ['major'], 6 | labels: ['type/major'], 7 | }, 8 | { 9 | matchUpdateTypes: ['minor'], 10 | labels: ['type/minor'], 11 | }, 12 | { 13 | matchUpdateTypes: ['patch'], 14 | labels: ['type/patch'], 15 | }, 16 | { 17 | matchDatasources: ['github-releases', 'github-tags'], 18 | addLabels: ['renovate/github-release'], 19 | }, 20 | { 21 | matchManagers: ['github-actions'], 22 | addLabels: ['renovate/github-action'], 23 | }, 24 | ], 25 | } 26 | -------------------------------------------------------------------------------- /.github/renovate/semanticCommits.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | packageRules: [ 4 | { 5 | matchDatasources: ['github-releases', 'github-tags'], 6 | matchUpdateTypes: ['major'], 7 | commitMessagePrefix: 'feat(github-release)!: ', 8 | }, 9 | { 10 | matchDatasources: ['github-releases', 'github-tags'], 11 | matchUpdateTypes: ['minor'], 12 | semanticCommitType: 'feat', 13 | semanticCommitScope: 'github-release', 14 | }, 15 | { 16 | matchDatasources: ['github-releases', 'github-tags'], 17 | matchUpdateTypes: ['patch'], 18 | semanticCommitType: 'fix', 19 | semanticCommitScope: 'github-release', 20 | }, 21 | { 22 | matchManagers: ['github-actions'], 23 | matchUpdateTypes: ['major'], 24 | commitMessagePrefix: 'feat(github-action)!: ', 25 | }, 26 | { 27 | matchManagers: ['github-actions'], 28 | matchUpdateTypes: ['minor'], 29 | semanticCommitType: 'feat', 30 | semanticCommitScope: 'github-action', 31 | }, 32 | { 33 | matchManagers: ['github-actions'], 34 | matchUpdateTypes: ['patch'], 35 | semanticCommitType: 'fix', 36 | semanticCommitScope: 'github-action', 37 | }, 38 | ], 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | - renovate/* 11 | pull_request: 12 | 13 | jobs: 14 | main: 15 | name: Nx Cloud - Main Job 16 | uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@c22bd1e5301b796cd1288c9e61a3cd3cdcdb37d0 # v0.15.0 17 | with: 18 | main-branch-name: main 19 | number-of-agents: 3 20 | init-commands: | 21 | yarn nx-cloud start-ci-run --stop-agents-after="build" --agent-count=3 22 | parallel-commands: | 23 | yarn nx-cloud record -- yarn nx format:check 24 | parallel-commands-on-agents: | 25 | yarn nx affected --target=lint --parallel=3 26 | yarn nx affected --target=build --parallel=3 27 | yarn nx affected --target=test --parallel=3 --ci --code-coverage 28 | secrets: 29 | NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} 30 | 31 | agents: 32 | name: Nx Cloud - Agents 33 | uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@c22bd1e5301b796cd1288c9e61a3cd3cdcdb37d0 # v0.15.0 34 | with: 35 | number-of-agents: 3 36 | secrets: 37 | NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/meta-labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Meta Labeler' 3 | 4 | on: 5 | workflow_dispatch: 6 | pull_request: 7 | branches: ['main'] 8 | 9 | permissions: 10 | checks: write 11 | contents: read 12 | pull-requests: write 13 | 14 | jobs: 15 | labeler: 16 | name: Labeler 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Labeler 20 | uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 # v4.3.0 21 | with: 22 | configuration-path: .github/labeler.yaml 23 | repo-token: '${{ secrets.GITHUB_TOKEN }}' 24 | -------------------------------------------------------------------------------- /.github/workflows/meta-sync-labels.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Meta Sync labels' 3 | 4 | on: 5 | workflow_dispatch: 6 | push: 7 | branches: ['main'] 8 | paths: ['.github/labels.yaml'] 9 | 10 | jobs: 11 | labels: 12 | name: Sync Labels 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 17 | 18 | - name: Sync Labels 19 | uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3 20 | with: 21 | config-file: .github/labels.yaml 22 | token: '${{ secrets.GITHUB_TOKEN }}' 23 | delete-other-labels: true 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | dist 5 | tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | .angular 42 | *.env* 43 | !*.env.example* 44 | 45 | .vuepress/.cache 46 | .vuepress/.temp 47 | .nx/cache -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | branch="$(git rev-parse --abbrev-ref HEAD)" 5 | if [ "$branch" = "main" ]; then 6 | npx --no-install commitlint -V --edit "$1" 7 | fi 8 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn lint-staged --concurrent false 5 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | branch="$(git rev-parse --abbrev-ref HEAD)" 5 | if [ "$branch" = "main" ]; then 6 | (exec < /dev/tty && node_modules/.bin/cz --hook) || true < /dev/null 7 | fi 8 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '{apps,libs}/**/*.{ts,js,html,json,scss,css,md}': [ 3 | 'nx affected:lint --uncommitted --fix true', 4 | // 'nx affected:test --uncommitted', 5 | ], 6 | '*.{ts,js,html,json,scss,css,md,yaml,yml}': ['nx format:write --base=main'], 7 | }; 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | /dist 3 | /coverage 4 | .angular 5 | .yarn 6 | *.hbs 7 | /.nx/cache -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "nrwl.angular-console", 4 | "esbenp.prettier-vscode", 5 | "firsttris.vscode-jest-runner", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vuepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defaultTheme } from 'vuepress'; 2 | 3 | module.exports = { 4 | lang: 'en-US', 5 | title: 'Nx NestJS Angular Boilerplate', 6 | description: 7 | 'A full stack template repository utilizing Nx, NestJS, and Angular', 8 | base: 9 | process.env.DEPLOY_ENV === 'gh-pages' 10 | ? '/nx-nestjs-angular-boilerplate/' 11 | : '/', 12 | theme: defaultTheme({ 13 | repo: 'wgd3/nx-nestjs-angular-boilerplate', 14 | navbar: [ 15 | { 16 | text: 'Home', 17 | link: '/', 18 | }, 19 | { 20 | text: 'The Full Stack Engineer', 21 | link: 'https://thefullstack.engineer', 22 | }, 23 | ], 24 | sidebarDepth: 1, 25 | sidebar: [ 26 | { 27 | text: 'Welcome', 28 | link: '/', 29 | }, 30 | { 31 | text: 'Guide', 32 | link: '/docs', 33 | children: ['/docs/GETTING_STARTED', '/docs/ARCHITECTURE'], 34 | }, 35 | ], 36 | }), 37 | }; 38 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | yarn-path ".yarn/releases/yarn-1.22.19.cjs" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Wallace Daniel 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 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To-Do Checklist 2 | 3 | ## Server 4 | 5 | ### Authentication and Authorization 6 | 7 | - [x] Incorporate social logins 8 | - [x] Google 9 | - [ ] Apple 10 | - [x] Add refresh token support 11 | - [x] track current refresh token in database 12 | - [x] add `/auth/email/refresh` route to refresh access token 13 | - [ ] 2FA support 14 | 15 | ### User Management 16 | 17 | - [x] add `isEmailVerified` property 18 | - [x] add email support 19 | - [x] account verification 20 | - [x] password reset 21 | - [ ] Switch to true RBAC 22 | - [x] Add user PATCH and DELETE routes 23 | 24 | ### API 25 | 26 | - [x] Add pagination support 27 | 28 | ### Database 29 | 30 | - [ ] Database migrations 31 | - [x] Database seeding (creating random users) 32 | 33 | ### Miscellaneous 34 | 35 | - [x] Add Sentry error reporting support 36 | - [ ] Winston logging 37 | - [x] Terminus health checks 38 | 39 | ## Documentation 40 | 41 | - [ ] Use VuePress to set up static docs site 42 | 43 | ## Repository 44 | 45 | - [ ] Dockerfile 46 | - [ ] Kubernetes manifests 47 | - [x] Update README with project details/acknowledgements 48 | - [x] Add Husky and lint-staged support 49 | -------------------------------------------------------------------------------- /apps/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/.gitkeep -------------------------------------------------------------------------------- /apps/admin-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/admin-e2e/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; 3 | 4 | export default defineConfig({ 5 | e2e: nxE2EPreset(__dirname), 6 | }); 7 | -------------------------------------------------------------------------------- /apps/admin-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "apps/admin-e2e/src", 5 | "projectType": "application", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/cypress:cypress", 9 | "options": { 10 | "cypressConfig": "apps/admin-e2e/cypress.config.ts", 11 | "devServerTarget": "admin:serve:development", 12 | "testingType": "e2e" 13 | }, 14 | "configurations": { 15 | "production": { 16 | "devServerTarget": "admin:serve:production" 17 | }, 18 | "ci": { 19 | "devServerTarget": "admin:serve-static" 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": ["apps/admin-e2e/**/*.{js,ts}"] 28 | } 29 | } 30 | }, 31 | "tags": [], 32 | "implicitDependencies": ["admin"] 33 | } 34 | -------------------------------------------------------------------------------- /apps/admin-e2e/src/e2e/app.cy.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from '../support/app.po'; 2 | 3 | describe('admin', () => { 4 | beforeEach(() => cy.visit('/')); 5 | 6 | it('should display welcome message', () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login('my-email@something.com', 'myPassword'); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains('Welcome admin'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/admin-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /apps/admin-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = () => cy.get('h1'); 2 | -------------------------------------------------------------------------------- /apps/admin-e2e/src/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | Cypress.on('uncaught:exception', (err) => { 20 | if (err.message.includes(`Cannot use 'import.meta' outside a module`)) { 21 | return false; 22 | } 23 | return true; 24 | }); 25 | -------------------------------------------------------------------------------- /apps/admin-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"], 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /apps/admin/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /apps/admin/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'admin', 4 | preset: '../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../coverage/apps/admin', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /apps/admin/module-federation.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'admin', 3 | exposes: { 4 | './Routes': 'apps/admin/src/app/remote-entry/entry.routes.ts', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/admin/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:3000", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/admin/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideHttpClient, withInterceptors } from '@angular/common/http'; 2 | import { ApplicationConfig, importProvidersFrom } from '@angular/core'; 3 | import { 4 | provideRouter, 5 | withEnabledBlockingInitialNavigation, 6 | } from '@angular/router'; 7 | import { jwtInterceptor } from '@libs/frontend/data-access-common'; 8 | import { 9 | errorTailorImports, 10 | provideErrorTailorConfig, 11 | } from '@ngneat/error-tailor'; 12 | import { HotToastModule } from '@ngneat/hot-toast'; 13 | 14 | import { appRoutes } from './app.routes'; 15 | 16 | export const appConfig: ApplicationConfig = { 17 | providers: [ 18 | provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), 19 | provideHttpClient(withInterceptors([jwtInterceptor])), 20 | importProvidersFrom(HotToastModule, errorTailorImports), 21 | provideErrorTailorConfig({}), 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /apps/admin/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | export const appRoutes: Route[] = [ 4 | { 5 | path: '', 6 | loadChildren: () => 7 | import('./remote-entry/entry.routes').then((m) => m.remoteRoutes), 8 | }, 9 | ]; 10 | -------------------------------------------------------------------------------- /apps/admin/src/app/remote-entry/entry.component.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component } from '@angular/core'; 3 | import { RouterModule } from '@angular/router'; 4 | 5 | import { NxWelcomeComponent } from './nx-welcome.component'; 6 | 7 | @Component({ 8 | standalone: true, 9 | imports: [CommonModule, NxWelcomeComponent, RouterModule], 10 | selector: 'nx-nestjs-angular-boilerplate-admin-entry', 11 | template: ``, 12 | }) 13 | export class RemoteEntryComponent {} 14 | -------------------------------------------------------------------------------- /apps/admin/src/app/remote-entry/entry.routes.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | import { authGuard } from '@libs/frontend/data-access-common'; 3 | 4 | export const remoteRoutes: Route[] = [ 5 | { 6 | path: '', 7 | // path: 'users', 8 | canActivateChild: [authGuard], 9 | loadChildren: () => 10 | import('@libs/frontend/feature-admin-uers').then( 11 | (m) => m.frontendFeatureAdminUsersRoutes, 12 | ), 13 | }, 14 | // { path: '', canActivate: [authGuard], component: RemoteEntryComponent }, 15 | ]; 16 | -------------------------------------------------------------------------------- /apps/admin/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/admin/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/admin/src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { RemoteEntryComponent } from './app/remote-entry/entry.component'; 4 | 5 | bootstrapApplication(RemoteEntryComponent, appConfig).catch((err) => 6 | console.error(err), 7 | ); 8 | -------------------------------------------------------------------------------- /apps/admin/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/admin/src/favicon.ico -------------------------------------------------------------------------------- /apps/admin/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | admin 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/admin/src/main.ts: -------------------------------------------------------------------------------- 1 | import('./bootstrap').catch((err) => console.error(err)); 2 | -------------------------------------------------------------------------------- /apps/admin/src/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* You can add global styles to this file, and also import other style files */ 6 | -------------------------------------------------------------------------------- /apps/admin/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /apps/admin/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); 2 | const { join } = require('path'); 3 | const { sharedTailwindConfig } = require('../../libs/shared/ui-tailwind/src'); 4 | 5 | /** @type {import('tailwindcss').Config} */ 6 | module.exports = { 7 | presets: [sharedTailwindConfig], 8 | 9 | content: [ 10 | join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), 11 | ...createGlobPatternsForDependencies(__dirname), 12 | ], 13 | // theme: { 14 | // extend: {}, 15 | // }, 16 | // plugins: [], 17 | }; 18 | -------------------------------------------------------------------------------- /apps/admin/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [], 6 | "target": "ES2020" 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"], 10 | "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/admin/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.app.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | }, 21 | { 22 | "path": "./tsconfig.editor.json" 23 | } 24 | ], 25 | "extends": "../../tsconfig.base.json", 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/admin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/admin/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { withModuleFederation } = require('@nx/angular/module-federation'); 2 | const config = require('./module-federation.config'); 3 | module.exports = withModuleFederation(config); 4 | -------------------------------------------------------------------------------- /apps/admin/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | const { withModuleFederation } = require('@nx/angular/module-federation'); 2 | const config = require('./module-federation.config'); 3 | module.exports = withModuleFederation({ 4 | ...config, 5 | /* 6 | * Remote overrides for production. 7 | * Each entry is a pair of a unique name and the URL where it is deployed. 8 | * 9 | * e.g. 10 | * remotes: [ 11 | * ['app1', 'https://app1.example.com'], 12 | * ['app2', 'https://app2.example.com'], 13 | * ] 14 | */ 15 | }); 16 | -------------------------------------------------------------------------------- /apps/client-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/client-e2e/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; 3 | 4 | export default defineConfig({ 5 | e2e: nxE2EPreset(__dirname), 6 | }); 7 | -------------------------------------------------------------------------------- /apps/client-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "apps/client-e2e/src", 5 | "projectType": "application", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/cypress:cypress", 9 | "options": { 10 | "cypressConfig": "apps/client-e2e/cypress.config.ts", 11 | "devServerTarget": "client:serve:development", 12 | "testingType": "e2e" 13 | }, 14 | "configurations": { 15 | "production": { 16 | "devServerTarget": "client:serve:production" 17 | }, 18 | "ci": { 19 | "devServerTarget": "client:serve-static" 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": ["apps/client-e2e/**/*.{js,ts}"] 28 | } 29 | } 30 | }, 31 | "tags": [], 32 | "implicitDependencies": ["client"] 33 | } 34 | -------------------------------------------------------------------------------- /apps/client-e2e/src/e2e/app.cy.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from '../support/app.po'; 2 | 3 | describe('client', () => { 4 | beforeEach(() => cy.visit('/')); 5 | 6 | it('should display welcome message', () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login('my-email@something.com', 'myPassword'); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains('Welcome client'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/client-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /apps/client-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = () => cy.get('h1'); 2 | -------------------------------------------------------------------------------- /apps/client-e2e/src/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | -------------------------------------------------------------------------------- /apps/client-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"], 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /apps/client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /apps/client/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'client', 4 | preset: '../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../coverage/apps/client', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /apps/client/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:3000", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/client/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

Client

2 | 3 | -------------------------------------------------------------------------------- /apps/client/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/client/src/app/app.component.scss -------------------------------------------------------------------------------- /apps/client/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { NxWelcomeComponent } from './nx-welcome.component'; 6 | 7 | describe('AppComponent', () => { 8 | beforeEach(async () => { 9 | await TestBed.configureTestingModule({ 10 | imports: [AppComponent, NxWelcomeComponent, RouterTestingModule], 11 | }).compileComponents(); 12 | }); 13 | 14 | it('should render title', () => { 15 | const fixture = TestBed.createComponent(AppComponent); 16 | fixture.detectChanges(); 17 | const compiled = fixture.nativeElement as HTMLElement; 18 | expect(compiled.querySelector('h1')?.textContent).toContain('Client'); 19 | }); 20 | 21 | it(`should have as title 'client'`, () => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | const app = fixture.componentInstance; 24 | expect(app.title).toEqual('client'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /apps/client/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { NxWelcomeComponent } from './nx-welcome.component'; 4 | 5 | @Component({ 6 | standalone: true, 7 | imports: [NxWelcomeComponent, RouterModule], 8 | selector: 'nx-nestjs-angular-boilerplate-root', 9 | templateUrl: './app.component.html', 10 | styleUrls: ['./app.component.scss'], 11 | }) 12 | export class AppComponent { 13 | title = 'client'; 14 | } 15 | -------------------------------------------------------------------------------- /apps/client/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideHttpClient } from '@angular/common/http'; 2 | import { ApplicationConfig, importProvidersFrom } from '@angular/core'; 3 | import { 4 | provideRouter, 5 | RouterModule, 6 | withEnabledBlockingInitialNavigation, 7 | } from '@angular/router'; 8 | 9 | import { appRoutes } from './app.routes'; 10 | 11 | export const appConfig: ApplicationConfig = { 12 | providers: [ 13 | provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), 14 | provideHttpClient(), 15 | importProvidersFrom(RouterModule), 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /apps/client/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | export const appRoutes: Route[] = [ 4 | { 5 | path: 'login', 6 | loadChildren: () => 7 | import('@libs/frontend/feature-login').then( 8 | (m) => m.frontendFeatureLoginRoutes, 9 | ), 10 | }, 11 | ]; 12 | -------------------------------------------------------------------------------- /apps/client/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/client/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/client/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/client/src/favicon.ico -------------------------------------------------------------------------------- /apps/client/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | client 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/client/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig).catch((err) => 6 | console.error(err), 7 | ); 8 | -------------------------------------------------------------------------------- /apps/client/src/styles.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* You can add global styles to this file, and also import other style files */ 6 | -------------------------------------------------------------------------------- /apps/client/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /apps/client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); 2 | const { join } = require('path'); 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | module.exports = { 6 | content: [ 7 | join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), 8 | ...createGlobPatternsForDependencies(__dirname), 9 | ], 10 | theme: { 11 | extend: {}, 12 | }, 13 | plugins: [], 14 | }; 15 | -------------------------------------------------------------------------------- /apps/client/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [] 6 | }, 7 | "files": ["src/main.ts"], 8 | "include": ["src/**/*.d.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/client/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.app.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | }, 21 | { 22 | "path": "./tsconfig.editor.json" 23 | } 24 | ], 25 | "extends": "../../tsconfig.base.json", 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/client/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/login-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/login-e2e/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; 3 | 4 | export default defineConfig({ 5 | e2e: nxE2EPreset(__dirname), 6 | }); 7 | -------------------------------------------------------------------------------- /apps/login-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "login-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "apps/login-e2e/src", 5 | "projectType": "application", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/cypress:cypress", 9 | "options": { 10 | "cypressConfig": "apps/login-e2e/cypress.config.ts", 11 | "devServerTarget": "login:serve:development", 12 | "testingType": "e2e" 13 | }, 14 | "configurations": { 15 | "production": { 16 | "devServerTarget": "login:serve:production" 17 | }, 18 | "ci": { 19 | "devServerTarget": "login:serve-static" 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": ["apps/login-e2e/**/*.{js,ts}"] 28 | } 29 | } 30 | }, 31 | "tags": [], 32 | "implicitDependencies": ["login"] 33 | } 34 | -------------------------------------------------------------------------------- /apps/login-e2e/src/e2e/app.cy.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from '../support/app.po'; 2 | 3 | describe('login', () => { 4 | beforeEach(() => cy.visit('/')); 5 | 6 | it('should display welcome message', () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login('my-email@something.com', 'myPassword'); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains('Welcome login'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/login-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /apps/login-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = () => cy.get('h1'); 2 | -------------------------------------------------------------------------------- /apps/login-e2e/src/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | Cypress.on('uncaught:exception', (err) => { 20 | if (err.message.includes(`Cannot use 'import.meta' outside a module`)) { 21 | return false; 22 | } 23 | return true; 24 | }); 25 | -------------------------------------------------------------------------------- /apps/login-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"], 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /apps/login/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /apps/login/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'login', 4 | preset: '../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../coverage/apps/login', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /apps/login/module-federation.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'login', 3 | exposes: { 4 | './Routes': 'apps/login/src/app/remote-entry/entry.routes.ts', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/login/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:3000", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/login/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideHttpClient } from '@angular/common/http'; 2 | import { ApplicationConfig, importProvidersFrom } from '@angular/core'; 3 | import { 4 | provideRouter, 5 | RouterModule, 6 | withEnabledBlockingInitialNavigation, 7 | } from '@angular/router'; 8 | 9 | import { appRoutes } from './app.routes'; 10 | 11 | export const appConfig: ApplicationConfig = { 12 | providers: [ 13 | provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), 14 | provideHttpClient(), 15 | importProvidersFrom(RouterModule), 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /apps/login/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | export const appRoutes: Route[] = [ 4 | { 5 | path: '', 6 | loadChildren: () => 7 | import('./remote-entry/entry.routes').then((m) => m.remoteRoutes), 8 | }, 9 | ]; 10 | -------------------------------------------------------------------------------- /apps/login/src/app/remote-entry/entry.routes.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | import { RemoteEntryComponent } from './entry.component'; 3 | 4 | export const remoteRoutes: Route[] = [ 5 | { path: '', component: RemoteEntryComponent }, 6 | ]; 7 | -------------------------------------------------------------------------------- /apps/login/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/login/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/login/src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { RemoteEntryComponent } from './app/remote-entry/entry.component'; 4 | 5 | bootstrapApplication(RemoteEntryComponent, appConfig).catch((err) => 6 | console.error(err), 7 | ); 8 | -------------------------------------------------------------------------------- /apps/login/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/login/src/favicon.ico -------------------------------------------------------------------------------- /apps/login/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | login 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/login/src/main.ts: -------------------------------------------------------------------------------- 1 | import('./bootstrap').catch((err) => console.error(err)); 2 | -------------------------------------------------------------------------------- /apps/login/src/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* You can add global styles to this file, and also import other style files */ 6 | html, 7 | body { 8 | height: 100vh; 9 | width: 100vw; 10 | position: relative; 11 | } 12 | -------------------------------------------------------------------------------- /apps/login/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /apps/login/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); 2 | const { join } = require('path'); 3 | const { sharedTailwindConfig } = require('../../libs/shared/ui-tailwind/src'); 4 | 5 | /** @type {import('tailwindcss').Config} */ 6 | module.exports = { 7 | presets: [sharedTailwindConfig], 8 | 9 | content: [ 10 | join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), 11 | ...createGlobPatternsForDependencies(__dirname), 12 | ], 13 | // theme: { 14 | // extend: {}, 15 | // }, 16 | plugins: [], 17 | }; 18 | -------------------------------------------------------------------------------- /apps/login/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [], 6 | "target": "ES2020" 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"], 10 | "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/login/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/login/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.app.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | }, 21 | { 22 | "path": "./tsconfig.editor.json" 23 | } 24 | ], 25 | "extends": "../../tsconfig.base.json", 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/login/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/login/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { withModuleFederation } = require('@nx/angular/module-federation'); 2 | const config = require('./module-federation.config'); 3 | module.exports = withModuleFederation(config); 4 | -------------------------------------------------------------------------------- /apps/login/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | const { withModuleFederation } = require('@nx/angular/module-federation'); 2 | const config = require('./module-federation.config'); 3 | module.exports = withModuleFederation({ 4 | ...config, 5 | /* 6 | * Remote overrides for production. 7 | * Each entry is a pair of a unique name and the URL where it is deployed. 8 | * 9 | * e.g. 10 | * remotes: [ 11 | * ['app1', 'https://app1.example.com'], 12 | * ['app2', 'https://app2.example.com'], 13 | * ] 14 | */ 15 | }); 16 | -------------------------------------------------------------------------------- /apps/server-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/server-e2e/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'server-e2e', 4 | preset: '../../jest.preset.js', 5 | globalSetup: '/src/support/global-setup.ts', 6 | globalTeardown: '/src/support/global-teardown.ts', 7 | setupFiles: ['/src/support/test-setup.ts'], 8 | testEnvironment: 'node', 9 | transform: { 10 | '^.+\\.[tj]s$': [ 11 | 'ts-jest', 12 | { 13 | tsconfig: '/tsconfig.spec.json', 14 | }, 15 | ], 16 | }, 17 | moduleFileExtensions: ['ts', 'js', 'html'], 18 | coverageDirectory: '../../coverage/server-e2e', 19 | }; 20 | -------------------------------------------------------------------------------- /apps/server-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "implicitDependencies": ["server"], 5 | "targets": { 6 | "e2e": { 7 | "executor": "@nx/jest:jest", 8 | "outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"], 9 | "options": { 10 | "jestConfig": "apps/server-e2e/jest.config.ts", 11 | "passWithNoTests": true 12 | } 13 | }, 14 | "lint": { 15 | "executor": "@nx/eslint:lint", 16 | "outputs": ["{options.outputFile}"], 17 | "options": { 18 | "lintFilePatterns": ["apps/server-e2e/**/*.{js,ts}"] 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/server-e2e/src/support/global-setup.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var __TEARDOWN_MESSAGE__: string; 3 | 4 | module.exports = async function () { 5 | // Start services that that the app needs to run (e.g. database, docker-compose, etc.). 6 | console.log('\nSetting up...\n'); 7 | 8 | // Hint: Use `globalThis` to pass variables to global teardown. 9 | globalThis.__TEARDOWN_MESSAGE__ = '\nTearing down...\n'; 10 | }; 11 | -------------------------------------------------------------------------------- /apps/server-e2e/src/support/global-teardown.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | module.exports = async function () { 4 | // Put clean up logic here (e.g. stopping services, docker-compose, etc.). 5 | // Hint: `globalThis` is shared between setup and teardown. 6 | console.log(globalThis.__TEARDOWN_MESSAGE__); 7 | }; 8 | -------------------------------------------------------------------------------- /apps/server-e2e/src/support/test-setup.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | import axios from 'axios'; 4 | 5 | module.exports = async function () { 6 | // Configure axios for tests to use. 7 | const host = process.env.HOST ?? 'localhost'; 8 | const port = process.env.PORT ?? '3000'; 9 | axios.defaults.baseURL = `http://${host}:${port}`; 10 | }; 11 | -------------------------------------------------------------------------------- /apps/server-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.spec.json" 8 | } 9 | ], 10 | "compilerOptions": { 11 | "esModuleInterop": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/server-e2e/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /apps/server/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'server', 4 | preset: '../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../coverage/apps/server', 11 | }; 12 | -------------------------------------------------------------------------------- /apps/server/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/server/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/server/src/assets/templates/contexts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './layout.email-context'; 2 | export * from './verify-email.email-context'; 3 | export * from './reset-password.email-context'; 4 | -------------------------------------------------------------------------------- /apps/server/src/assets/templates/contexts/layout.email-context.ts: -------------------------------------------------------------------------------- 1 | export interface IBaseEmailContext { 2 | title: string; 3 | username: string; 4 | } 5 | -------------------------------------------------------------------------------- /apps/server/src/assets/templates/contexts/reset-password.email-context.ts: -------------------------------------------------------------------------------- 1 | import { IBaseEmailContext } from './layout.email-context'; 2 | 3 | export interface IResetPasswordEmailContext extends IBaseEmailContext { 4 | passwordResetLink: string; 5 | } 6 | -------------------------------------------------------------------------------- /apps/server/src/assets/templates/contexts/verify-email.email-context.ts: -------------------------------------------------------------------------------- 1 | import { IBaseEmailContext } from './layout.email-context'; 2 | 3 | export interface IVerifyEmailContext extends IBaseEmailContext { 4 | verificationLink: string; 5 | userEmailAddress: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/server/src/assets/templates/partials/footer.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/server/src/assets/templates/partials/header.hbs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/server/src/assets/templates/partials/header.hbs -------------------------------------------------------------------------------- /apps/server/src/assets/templates/reset-password.hbs: -------------------------------------------------------------------------------- 1 | {{#> layout }} 2 |

We have received your request to reset your password. Click the link below to get started:

3 | Reset Password 4 | {{/layout}} -------------------------------------------------------------------------------- /apps/server/src/assets/templates/verify-email.hbs: -------------------------------------------------------------------------------- 1 | {{#> layout }} 2 |

Thanks for signing up! Please use the following link to verify your email:

3 | Verify {{ userEmailAddress }} 4 | {{/layout}} -------------------------------------------------------------------------------- /apps/server/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["node"], 7 | "emitDecoratorMetadata": true, 8 | "target": "es2021", 9 | "strictNullChecks": true, 10 | "noImplicitAny": true, 11 | "strictBindCallApply": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], 16 | "include": ["src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /apps/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "esModuleInterop": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/server/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/server/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { composePlugins, withNx } = require('@nx/webpack'); 2 | 3 | // Nx plugins for webpack. 4 | module.exports = composePlugins(withNx(), (config) => { 5 | // Update the webpack config as needed here. 6 | // e.g. `config.plugins.push(new MyPlugin())` 7 | return config; 8 | }); 9 | -------------------------------------------------------------------------------- /apps/shell-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/shell-e2e/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; 3 | 4 | export default defineConfig({ 5 | e2e: nxE2EPreset(__dirname), 6 | }); 7 | -------------------------------------------------------------------------------- /apps/shell-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shell-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "apps/shell-e2e/src", 5 | "projectType": "application", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/cypress:cypress", 9 | "options": { 10 | "cypressConfig": "apps/shell-e2e/cypress.config.ts", 11 | "devServerTarget": "shell:serve:development", 12 | "testingType": "e2e" 13 | }, 14 | "configurations": { 15 | "production": { 16 | "devServerTarget": "shell:serve:production" 17 | }, 18 | "ci": { 19 | "devServerTarget": "shell:serve-static" 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": ["apps/shell-e2e/**/*.{js,ts}"] 28 | } 29 | } 30 | }, 31 | "tags": [], 32 | "implicitDependencies": ["shell"] 33 | } 34 | -------------------------------------------------------------------------------- /apps/shell-e2e/src/e2e/app.cy.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from '../support/app.po'; 2 | 3 | describe('shell', () => { 4 | beforeEach(() => cy.visit('/')); 5 | 6 | it('should display welcome message', () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login('my-email@something.com', 'myPassword'); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains('Welcome shell'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/shell-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /apps/shell-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = () => cy.get('h1'); 2 | -------------------------------------------------------------------------------- /apps/shell-e2e/src/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | Cypress.on('uncaught:exception', (err) => { 20 | if (err.message.includes(`Cannot use 'import.meta' outside a module`)) { 21 | return false; 22 | } 23 | return true; 24 | }); 25 | -------------------------------------------------------------------------------- /apps/shell-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"], 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /apps/shell/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /apps/shell/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'shell', 4 | preset: '../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../coverage/apps/shell', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /apps/shell/module-federation.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'shell', 3 | remotes: [], 4 | }; 5 | -------------------------------------------------------------------------------- /apps/shell/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:3000", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/shell/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/shell/src/app/app.component.css -------------------------------------------------------------------------------- /apps/shell/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { NxWelcomeComponent } from './nx-welcome.component'; 6 | 7 | describe('AppComponent', () => { 8 | beforeEach(async () => { 9 | await TestBed.configureTestingModule({ 10 | imports: [ 11 | RouterTestingModule.withRoutes([ 12 | { path: '', component: NxWelcomeComponent }, 13 | ]), 14 | AppComponent, 15 | NxWelcomeComponent, 16 | ], 17 | declarations: [], 18 | }).compileComponents(); 19 | }); 20 | 21 | it('should create the app', () => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | const app = fixture.componentInstance; 24 | expect(app).toBeTruthy(); 25 | }); 26 | 27 | it(`should have as title 'shell'`, () => { 28 | const fixture = TestBed.createComponent(AppComponent); 29 | const app = fixture.componentInstance; 30 | expect(app.title).toEqual('shell'); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /apps/shell/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import { NxWelcomeComponent } from './nx-welcome.component'; 5 | 6 | @Component({ 7 | standalone: true, 8 | imports: [NxWelcomeComponent, RouterModule], 9 | selector: 'nx-nestjs-angular-boilerplate-root', 10 | templateUrl: './app.component.html', 11 | styleUrls: ['./app.component.css'], 12 | }) 13 | export class AppComponent { 14 | title = 'shell'; 15 | } 16 | -------------------------------------------------------------------------------- /apps/shell/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideHttpClient, withInterceptors } from '@angular/common/http'; 2 | import { ApplicationConfig } from '@angular/core'; 3 | import { 4 | provideRouter, 5 | withEnabledBlockingInitialNavigation, 6 | } from '@angular/router'; 7 | import { jwtInterceptor } from '@libs/frontend/data-access-common'; 8 | 9 | import { appRoutes } from './app.routes'; 10 | 11 | export const appConfig: ApplicationConfig = { 12 | providers: [ 13 | provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), 14 | provideHttpClient(withInterceptors([jwtInterceptor])), 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /apps/shell/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { NxWelcomeComponent } from './nx-welcome.component'; 2 | import { Route } from '@angular/router'; 3 | import { loadRemoteModule } from '@nx/angular/mf'; 4 | 5 | export const appRoutes: Route[] = [ 6 | { 7 | path: 'admin', 8 | loadChildren: () => 9 | loadRemoteModule('admin', './Routes').then((m) => m.remoteRoutes), 10 | }, 11 | { 12 | path: 'login', 13 | loadChildren: () => 14 | loadRemoteModule('login', './Routes').then((m) => m.remoteRoutes), 15 | }, 16 | { 17 | path: '', 18 | component: NxWelcomeComponent, 19 | }, 20 | ]; 21 | -------------------------------------------------------------------------------- /apps/shell/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/shell/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/shell/src/assets/module-federation.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": "http://localhost:4201", 3 | "admin": "http://localhost:4202" 4 | } 5 | -------------------------------------------------------------------------------- /apps/shell/src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig).catch((err) => 6 | console.error(err), 7 | ); 8 | -------------------------------------------------------------------------------- /apps/shell/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/apps/shell/src/favicon.ico -------------------------------------------------------------------------------- /apps/shell/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | shell 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/shell/src/main.ts: -------------------------------------------------------------------------------- 1 | import { setRemoteDefinitions } from '@nx/angular/mf'; 2 | 3 | fetch('/assets/module-federation.manifest.json') 4 | .then((res) => res.json()) 5 | .then((definitions) => setRemoteDefinitions(definitions)) 6 | .then(() => import('./bootstrap').catch((err) => console.error(err))); 7 | -------------------------------------------------------------------------------- /apps/shell/src/remotes.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'login/Routes'; 2 | declare module 'admin/Routes'; 3 | -------------------------------------------------------------------------------- /apps/shell/src/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* You can add global styles to this file, and also import other style files */ 6 | html, 7 | body { 8 | width: 100vw; 9 | height: 100vh; 10 | position: relative; 11 | } 12 | -------------------------------------------------------------------------------- /apps/shell/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /apps/shell/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); 2 | const { join } = require('path'); 3 | const { sharedTailwindConfig } = require('../../libs/shared/ui-tailwind/src'); 4 | 5 | /** @type {import('tailwindcss').Config} */ 6 | module.exports = { 7 | presets: [sharedTailwindConfig], 8 | content: [ 9 | join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), 10 | ...createGlobPatternsForDependencies(__dirname), 11 | // https://github.com/nrwl/nx/issues/9784 12 | join(__dirname, '../admin/src/**/!(*.stories|*.spec).{ts,html}'), 13 | join(__dirname, '../login/src/**/!(*.stories|*.spec).{ts,html}'), 14 | // don't forget the libraries! 15 | join(__dirname, '../../libs/frontend/**/!(*.stories|*.spec).{ts,html}'), 16 | ], 17 | // theme: { 18 | // extend: {}, 19 | // }, 20 | // plugins: [], 21 | }; 22 | -------------------------------------------------------------------------------- /apps/shell/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [], 6 | "target": "ES2020" 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"], 10 | "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/shell/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/shell/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.app.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | }, 21 | { 22 | "path": "./tsconfig.editor.json" 23 | } 24 | ], 25 | "extends": "../../tsconfig.base.json", 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/shell/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/shell/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { withModuleFederation } = require('@nx/angular/module-federation'); 2 | const config = require('./module-federation.config'); 3 | module.exports = withModuleFederation({ 4 | ...config, 5 | additionalShared: [ 6 | ['@angular/common', { singleton: true }], 7 | ['@angular/core', { singleton: true }], 8 | ['@angular/router', { singleton: true }], 9 | ['@libs/frontend/data-access-common', { singleton: true }], 10 | ], 11 | }); 12 | -------------------------------------------------------------------------------- /docs/ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Architecture 3 | description: An overview of the boilerplate repository 4 | --- 5 | 6 | # Architecture 7 | 8 | ## Repository Overview 9 | 10 | ```sh 11 | ├── apps 12 | │ ├── client 13 | │ ├── client-e2e 14 | │ ├── server 15 | │ └── server-e2e 16 | ├── assets 17 | ├── dist 18 | │ ├── apps 19 | │ └── cypress 20 | ├── docs 21 | ├── libs 22 | │ ├── server 23 | │ └── shared 24 | └── tools 25 | ``` 26 | 27 | ## Methodology 28 | -------------------------------------------------------------------------------- /docs/FRONTEND.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Frontend Design and Architecture 3 | --- 4 | 5 | # Frontend Design and Architecture 6 | 7 | The frontend in thie repository was established using dynamic module federation in order to support an arbitrary number of additional client applications. It also means that multiple frameworks (React, Vue, etc) can be used in tandem with Angular. 8 | 9 | ## Scaffolding 10 | 11 | ```sh 12 | > nx generate @nx/angular:host --name=shell \ 13 | --remotes=login,admin \ 14 | --addTailwind=true \ 15 | --backendProject=server \ 16 | --dynamic=true \ 17 | --standalone=true \ 18 | --tags=type:app,scope:frontend,framework:angular 19 | ``` 20 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import { getJestProjects } from '@nx/jest'; 2 | 3 | export default { 4 | projects: getJestProjects(), 5 | }; 6 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nx/jest/preset').default; 2 | 3 | module.exports = { ...nxPreset }; 4 | -------------------------------------------------------------------------------- /libs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/libs/.gitkeep -------------------------------------------------------------------------------- /libs/frontend/data-access-common/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/README.md: -------------------------------------------------------------------------------- 1 | # frontend-data-access-common 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test frontend-data-access-common` to execute the unit tests. 8 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'frontend-data-access-common', 4 | preset: '../../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../../coverage/libs/frontend/data-access-common', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend-data-access-common", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/frontend/data-access-common/src", 5 | "prefix": "nx-nestjs-angular-boilerplate", 6 | "tags": ["type:data-access", "scope:frontend", "framework:angular"], 7 | "projectType": "library", 8 | "targets": { 9 | "test": { 10 | "executor": "@nx/jest:jest", 11 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 12 | "options": { 13 | "jestConfig": "libs/frontend/data-access-common/jest.config.ts", 14 | "passWithNoTests": true 15 | }, 16 | "configurations": { 17 | "ci": { 18 | "ci": true, 19 | "codeCoverage": true 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": [ 28 | "libs/frontend/data-access-common/**/*.ts", 29 | "libs/frontend/data-access-common/**/*.html" 30 | ] 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/interceptors/jwt.interceptor'; 2 | export * from './lib/guards/auth-guard.guard'; 3 | export * from './lib/constants'; 4 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const REDIRECT_URL_QUERY_PARAM = 'redirectUrl'; 2 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/src/lib/guards/auth-guard.guard.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { CanActivateFn } from '@angular/router'; 3 | 4 | import { authGuard } from './auth-guard.guard'; 5 | 6 | describe('authGuardGuard', () => { 7 | const executeGuard: CanActivateFn = (...guardParameters) => 8 | TestBed.runInInjectionContext(() => authGuard(...guardParameters)); 9 | 10 | beforeEach(() => { 11 | TestBed.configureTestingModule({}); 12 | }); 13 | 14 | it('should be created', () => { 15 | expect(executeGuard).toBeTruthy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/src/lib/guards/auth-guard.guard.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '@angular/core'; 2 | import { CanActivateFn, Router } from '@angular/router'; 3 | import { UserService } from '@libs/frontend/data-access-user'; 4 | 5 | import { REDIRECT_URL_QUERY_PARAM } from '../constants'; 6 | 7 | export const authGuard: CanActivateFn = (route, state) => { 8 | const userService = inject(UserService); 9 | const router = inject(Router); 10 | const expired = userService.tokenExpired; 11 | console.log(`[AuthGuard] token expired: ${expired}`); 12 | if (expired) { 13 | router.navigate(['/login'], { 14 | queryParams: { [REDIRECT_URL_QUERY_PARAM]: state.url }, 15 | }); 16 | return false; 17 | } 18 | return true; 19 | }; 20 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/src/lib/interceptors/jwt.interceptor.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { HttpInterceptorFn } from '@angular/common/http'; 3 | 4 | import { jwtInterceptor } from './jwt.interceptor'; 5 | 6 | describe('jwtInterceptor', () => { 7 | const interceptor: HttpInterceptorFn = (req, next) => 8 | TestBed.runInInjectionContext(() => jwtInterceptor(req, next)); 9 | 10 | beforeEach(() => { 11 | TestBed.configureTestingModule({}); 12 | }); 13 | 14 | it('should be created', () => { 15 | expect(interceptor).toBeTruthy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/src/lib/interceptors/jwt.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpInterceptorFn } from '@angular/common/http'; 2 | import { inject } from '@angular/core'; 3 | import { UserService } from '@libs/frontend/data-access-user'; 4 | 5 | export const jwtInterceptor: HttpInterceptorFn = (req, next) => { 6 | const accessToken = inject(UserService).accessToken; 7 | if (accessToken) { 8 | console.log( 9 | `[jwtInterceptor] Found access token: ${accessToken.slice(0, 12)}`, 10 | ); 11 | req = req.clone({ 12 | url: req.urlWithParams, 13 | setHeaders: { 14 | Authorization: `Bearer ${accessToken}`, 15 | }, 16 | }); 17 | } 18 | return next(req); 19 | }; 20 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ], 22 | "extends": "../../../tsconfig.base.json", 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "src/**/*.spec.ts", 12 | "src/test-setup.ts", 13 | "jest.config.ts", 14 | "src/**/*.test.ts" 15 | ], 16 | "include": ["src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /libs/frontend/data-access-common/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'frontend-data-access-user', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/frontend/data-access-user', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend-data-access-user", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/frontend/data-access-user/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/frontend/data-access-user/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 17 | "options": { 18 | "jestConfig": "libs/frontend/data-access-user/jest.config.ts", 19 | "passWithNoTests": true 20 | }, 21 | "configurations": { 22 | "ci": { 23 | "ci": true, 24 | "codeCoverage": true 25 | } 26 | } 27 | } 28 | }, 29 | "tags": ["type:data-access", "scope:frontend"] 30 | } 31 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/user-state'; 2 | export * from './lib/constants'; 3 | export * from './lib/user.service'; 4 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const LOCAL_STORAGE_KEY_ACCESS_TOKEN = `app-access-token`; 2 | export const LOCAL_STORAGE_KEY_REFRESH_TOKEN = `app-refresh-token`; 3 | 4 | export const LOADING_FOR_ADD_USER = 'add-user'; 5 | export const LOADING_FOR_UPDATE_USER = 'update-user'; 6 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/frontend/data-access-user/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/README.md: -------------------------------------------------------------------------------- 1 | # frontend-feature-admin-users 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test frontend-feature-admin-users` to execute the unit tests. 8 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'frontend-feature-admin-users', 4 | preset: '../../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../../coverage/libs/frontend/feature-admin-users', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend-feature-admin-users", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/frontend/feature-admin-users/src", 5 | "prefix": "nx-nestjs-angular-boilerplate", 6 | "tags": ["type:feature", "scope:frontend", "framework:angular"], 7 | "projectType": "library", 8 | "targets": { 9 | "test": { 10 | "executor": "@nx/jest:jest", 11 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 12 | "options": { 13 | "jestConfig": "libs/frontend/feature-admin-users/jest.config.ts", 14 | "passWithNoTests": true 15 | }, 16 | "configurations": { 17 | "ci": { 18 | "ci": true, 19 | "codeCoverage": true 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": [ 28 | "libs/frontend/feature-admin-users/**/*.ts", 29 | "libs/frontend/feature-admin-users/**/*.html" 30 | ] 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/lib.routes'; 2 | 3 | export * from './lib/frontend-feature-admin-users/frontend-feature-admin-users.component'; 4 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/src/lib/frontend-feature-admin-users/frontend-feature-admin-users.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/libs/frontend/feature-admin-users/src/lib/frontend-feature-admin-users/frontend-feature-admin-users.component.css -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/src/lib/frontend-feature-admin-users/frontend-feature-admin-users.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientModule } from '@angular/common/http'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { FrontendFeatureAdminUsersComponent } from './frontend-feature-admin-users.component'; 5 | 6 | describe('FrontendFeatureAdminUsersComponent', () => { 7 | let component: FrontendFeatureAdminUsersComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | imports: [FrontendFeatureAdminUsersComponent, HttpClientModule], 13 | }).compileComponents(); 14 | 15 | fixture = TestBed.createComponent(FrontendFeatureAdminUsersComponent); 16 | component = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should create', () => { 21 | expect(component).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/src/lib/lib.routes.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | import { FrontendFeatureAdminUsersComponent } from './frontend-feature-admin-users/frontend-feature-admin-users.component'; 3 | 4 | export const frontendFeatureAdminUsersRoutes: Route[] = [ 5 | { path: '', component: FrontendFeatureAdminUsersComponent }, 6 | ]; 7 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/src/lib/user-upsert-dialog/user-upsert-dialog.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/libs/frontend/feature-admin-users/src/lib/user-upsert-dialog/user-upsert-dialog.component.scss -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ], 22 | "extends": "../../../tsconfig.base.json", 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "src/**/*.spec.ts", 12 | "src/test-setup.ts", 13 | "jest.config.ts", 14 | "src/**/*.test.ts" 15 | ], 16 | "include": ["src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /libs/frontend/feature-admin-users/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/README.md: -------------------------------------------------------------------------------- 1 | # frontend-feature-login 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test frontend-feature-login` to execute the unit tests. 8 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'frontend-feature-login', 4 | preset: '../../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../../coverage/libs/frontend/feature-login', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend-feature-login", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/frontend/feature-login/src", 5 | "prefix": "nx-nestjs-angular-boilerplate", 6 | "tags": ["type:feature", "scope:frontend", "framework:angular"], 7 | "projectType": "library", 8 | "targets": { 9 | "test": { 10 | "executor": "@nx/jest:jest", 11 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 12 | "options": { 13 | "jestConfig": "libs/frontend/feature-login/jest.config.ts", 14 | "passWithNoTests": true 15 | }, 16 | "configurations": { 17 | "ci": { 18 | "ci": true, 19 | "codeCoverage": true 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": [ 28 | "libs/frontend/feature-login/**/*.ts", 29 | "libs/frontend/feature-login/**/*.html" 30 | ] 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/lib.routes'; 2 | 3 | export * from './lib/frontend-feature-login/frontend-feature-login.component'; 4 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/src/lib/frontend-feature-login/frontend-feature-login.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/libs/frontend/feature-login/src/lib/frontend-feature-login/frontend-feature-login.component.scss -------------------------------------------------------------------------------- /libs/frontend/feature-login/src/lib/frontend-feature-login/frontend-feature-login.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientModule } from '@angular/common/http'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { ActivatedRoute, RouterModule } from '@angular/router'; 4 | 5 | import { FrontendFeatureLoginComponent } from './frontend-feature-login.component'; 6 | 7 | describe('FrontendFeatureLoginComponent', () => { 8 | let component: FrontendFeatureLoginComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | imports: [FrontendFeatureLoginComponent, HttpClientModule, RouterModule], 14 | providers: [{ provide: ActivatedRoute, useValue: {} }], 15 | }).compileComponents(); 16 | 17 | fixture = TestBed.createComponent(FrontendFeatureLoginComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/src/lib/lib.routes.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | import { FrontendFeatureLoginComponent } from './frontend-feature-login/frontend-feature-login.component'; 3 | 4 | export const frontendFeatureLoginRoutes: Route[] = [ 5 | { path: '', component: FrontendFeatureLoginComponent }, 6 | ]; 7 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ], 22 | "extends": "../../../tsconfig.base.json", 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "src/**/*.spec.ts", 12 | "src/test-setup.ts", 13 | "jest.config.ts", 14 | "src/**/*.test.ts" 15 | ], 16 | "include": ["src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /libs/frontend/feature-login/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /libs/frontend/util-common/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "rules": { 8 | "@angular-eslint/directive-selector": [ 9 | "error", 10 | { 11 | "type": "attribute", 12 | "prefix": "nxNestjsAngularBoilerplate", 13 | "style": "camelCase" 14 | } 15 | ], 16 | "@angular-eslint/component-selector": [ 17 | "error", 18 | { 19 | "type": "element", 20 | "prefix": "nx-nestjs-angular-boilerplate", 21 | "style": "kebab-case" 22 | } 23 | ] 24 | }, 25 | "extends": [ 26 | "plugin:@nx/angular", 27 | "plugin:@angular-eslint/template/process-inline-templates" 28 | ] 29 | }, 30 | { 31 | "files": ["*.html"], 32 | "extends": ["plugin:@nx/angular-template"], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /libs/frontend/util-common/README.md: -------------------------------------------------------------------------------- 1 | # frontend-util-common 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test frontend-util-common` to execute the unit tests. 8 | -------------------------------------------------------------------------------- /libs/frontend/util-common/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'frontend-util-common', 4 | preset: '../../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | coverageDirectory: '../../../coverage/libs/frontend/util-common', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /libs/frontend/util-common/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend-util-common", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/frontend/util-common/src", 5 | "prefix": "nx-nestjs-angular-boilerplate", 6 | "tags": ["type:util", "scope:frontend", "framework:angular"], 7 | "projectType": "library", 8 | "targets": { 9 | "test": { 10 | "executor": "@nx/jest:jest", 11 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 12 | "options": { 13 | "jestConfig": "libs/frontend/util-common/jest.config.ts", 14 | "passWithNoTests": true 15 | }, 16 | "configurations": { 17 | "ci": { 18 | "ci": true, 19 | "codeCoverage": true 20 | } 21 | } 22 | }, 23 | "lint": { 24 | "executor": "@nx/eslint:lint", 25 | "outputs": ["{options.outputFile}"], 26 | "options": { 27 | "lintFilePatterns": [ 28 | "libs/frontend/util-common/**/*.ts", 29 | "libs/frontend/util-common/**/*.html" 30 | ] 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libs/frontend/util-common/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/directives'; 2 | -------------------------------------------------------------------------------- /libs/frontend/util-common/src/lib/directives/index.ts: -------------------------------------------------------------------------------- 1 | export * from './upsert-dialog-directive/upsert-dialog.directive'; 2 | export * from './upsert-dialog-directive/upsert-dialog-data.interface'; 3 | -------------------------------------------------------------------------------- /libs/frontend/util-common/src/lib/directives/upsert-dialog-directive/upsert-dialog-data.interface.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | 3 | export type UpsertDialogDataActionType = 'add' | 'edit'; 4 | 5 | export interface UpsertDialogData { 6 | action: UpsertDialogDataActionType; 7 | actions$: Record< 8 | UpsertDialogDataActionType, 9 | (value: unknown, ...args: unknown[]) => Observable 10 | >; 11 | currentValue: T | undefined; 12 | error: string | undefined; 13 | } 14 | -------------------------------------------------------------------------------- /libs/frontend/util-common/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment 2 | globalThis.ngJest = { 3 | testEnvironmentOptions: { 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }, 7 | }; 8 | import 'jest-preset-angular/setup-jest'; 9 | -------------------------------------------------------------------------------- /libs/frontend/util-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ], 22 | "extends": "../../../tsconfig.base.json", 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libs/frontend/util-common/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "src/**/*.spec.ts", 12 | "src/test-setup.ts", 13 | "jest.config.ts", 14 | "src/**/*.test.ts" 15 | ], 16 | "include": ["src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /libs/frontend/util-common/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/data-access/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/server/data-access/README.md: -------------------------------------------------------------------------------- 1 | # server-data-access 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test server-data-access` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/server/data-access/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'server-data-access', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/server/data-access', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/server/data-access/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-data-access", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/server/data-access/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/server/data-access/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 17 | "options": { 18 | "jestConfig": "libs/server/data-access/jest.config.ts", 19 | "passWithNoTests": true 20 | }, 21 | "configurations": { 22 | "ci": { 23 | "ci": true, 24 | "codeCoverage": true 25 | } 26 | } 27 | } 28 | }, 29 | "tags": ["type:data-access", "scope:server"] 30 | } 31 | -------------------------------------------------------------------------------- /libs/server/data-access/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/server-data-access.module'; 2 | export * from './lib/database/entities/user.orm-entity'; 3 | export * from './lib/dtos'; 4 | export * from './lib/api-page-ok-response.decorator'; 5 | export * from './lib/database/pagination.service'; 6 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/api-page-ok-response.decorator.ts: -------------------------------------------------------------------------------- 1 | import type { Type } from '@nestjs/common'; 2 | import { applyDecorators } from '@nestjs/common'; 3 | import { ApiExtraModels, ApiOkResponse, getSchemaPath } from '@nestjs/swagger'; 4 | 5 | import { PaginationResponseDto } from './dtos/pagination/pagination-response.dto'; 6 | 7 | /** 8 | * Credit to https://github.com/NarHakobyan/awesome-nest-boilerplate/blob/0ebc649acc1e3406bf13684121f2471ee329cc3e/src/decorators/api-page-ok-response.decorator.ts 9 | * @param options 10 | * @returns 11 | */ 12 | export function ApiPageOkResponse(options: { 13 | type: T; 14 | description?: string; 15 | }): MethodDecorator { 16 | return applyDecorators( 17 | ApiExtraModels(PaginationResponseDto), 18 | ApiExtraModels(options.type), 19 | ApiOkResponse({ 20 | description: options.description, 21 | schema: { 22 | allOf: [ 23 | { $ref: getSchemaPath(PaginationResponseDto) }, 24 | { 25 | properties: { 26 | data: { 27 | type: 'array', 28 | items: { $ref: getSchemaPath(options.type) }, 29 | }, 30 | }, 31 | }, 32 | ], 33 | }, 34 | }), 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/database/database.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, Provider } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { EntityClassOrSchema } from '@nestjs/typeorm/dist/interfaces/entity-class-or-schema.type'; 4 | 5 | import { UserOrmEntity } from './entities/user.orm-entity'; 6 | import { UserOrmEntitySubscriber } from './subscribers/user.entity-subscriber'; 7 | 8 | const ormEventSubscribers: Provider[] = [UserOrmEntitySubscriber]; 9 | 10 | const entities: EntityClassOrSchema[] = [UserOrmEntity]; 11 | 12 | const typeormModule = TypeOrmModule.forFeature(entities); 13 | 14 | @Module({ 15 | imports: [typeormModule], 16 | providers: [...ormEventSubscribers], 17 | exports: [typeormModule], 18 | }) 19 | export class DatabaseModule {} 20 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/database/seeds/seed-database.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | 3 | import { SeedModule } from './seed.module'; 4 | import { UserDatabaseSeedService } from './users.database-seed'; 5 | 6 | const seedDatabase = async () => { 7 | const app = await NestFactory.create(SeedModule); 8 | 9 | await app.get(UserDatabaseSeedService).seed(); 10 | await app.get(UserDatabaseSeedService).populateRandomUsers(20); 11 | 12 | await app.close(); 13 | }; 14 | 15 | void seedDatabase(); 16 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/database/seeds/seed.module.ts: -------------------------------------------------------------------------------- 1 | import { 2 | appConfig, 3 | dbConfig, 4 | googleConfig, 5 | mailerConfig, 6 | TypeormConfigService, 7 | validationSchema, 8 | } from '@libs/server/util-common'; 9 | import { Module } from '@nestjs/common'; 10 | import { ConfigModule, ConfigService } from '@nestjs/config'; 11 | import { TypeOrmModule } from '@nestjs/typeorm'; 12 | 13 | import { DatabaseModule } from '../database.module'; 14 | import { UserDatabaseSeedService } from './users.database-seed'; 15 | 16 | @Module({ 17 | imports: [ 18 | ConfigModule.forRoot({ 19 | isGlobal: true, 20 | envFilePath: '.env', 21 | load: [appConfig, dbConfig, googleConfig, mailerConfig], 22 | validationSchema, 23 | }), 24 | TypeOrmModule.forRootAsync({ 25 | useClass: TypeormConfigService, 26 | inject: [ConfigService], 27 | }), 28 | DatabaseModule, 29 | ], 30 | providers: [UserDatabaseSeedService], 31 | }) 32 | export class SeedModule {} 33 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/auth/forgot-password.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsEmail } from 'class-validator'; 2 | 3 | import { IForgotPasswordPayload } from '@libs/shared/util-types'; 4 | import { ApiProperty } from '@nestjs/swagger'; 5 | 6 | export class ForgotPasswordDto implements IForgotPasswordPayload { 7 | @IsEmail() 8 | @ApiProperty({ 9 | type: String, 10 | example: 'foo@bar.com', 11 | }) 12 | email!: string; 13 | } 14 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './token-response.dto'; 2 | export * from './forgot-password.dto'; 3 | export * from './reset-password.dto'; 4 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/auth/token-response.dto.ts: -------------------------------------------------------------------------------- 1 | import { ITokenResponse } from '@libs/shared/util-types'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | 4 | export class TokenResponseDto implements ITokenResponse { 5 | @ApiProperty() 6 | accessToken!: string; 7 | 8 | @ApiProperty() 9 | refreshToken!: string; 10 | 11 | constructor(data: { accessToken: string; refreshToken: string }) { 12 | this.accessToken = data.accessToken; 13 | this.refreshToken = data.refreshToken; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/index.ts: -------------------------------------------------------------------------------- 1 | export * from './users/create-user.dto'; 2 | export * from './users/login-user.dto'; 3 | export * from './users/user.dto'; 4 | export * from './auth'; 5 | export * from './users/update-user.dto'; 6 | export * from './pagination'; 7 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/pagination/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pagination-meta.dto'; 2 | export * from './pagination-response.dto'; 3 | export * from './pagination-options.dto'; 4 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/pagination/pagination-response.dto.ts: -------------------------------------------------------------------------------- 1 | import { IPaginatedResponse } from '@libs/shared/util-types'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | 4 | import { PaginationMetaDto } from './pagination-meta.dto'; 5 | 6 | export class PaginationResponseDto implements IPaginatedResponse { 7 | @ApiProperty() 8 | meta: PaginationMetaDto; 9 | 10 | @ApiProperty() 11 | data: T[]; 12 | 13 | constructor(data: T[], meta: PaginationMetaDto) { 14 | this.meta = meta; 15 | this.data = data; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/users/login-user.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsEmail, IsString } from 'class-validator'; 2 | 3 | import { IUserLogin } from '@libs/shared/util-types'; 4 | import { ApiProperty } from '@nestjs/swagger'; 5 | 6 | export class UserLoginDto implements IUserLogin { 7 | @ApiProperty({ 8 | type: String, 9 | example: `wallace@thefullstack.engineer`, 10 | }) 11 | @IsEmail() 12 | email!: string; 13 | 14 | @ApiProperty({ 15 | type: String, 16 | required: true, 17 | example: 'Password1!', 18 | }) 19 | @IsString() 20 | password!: string; 21 | } 22 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/users/update-user.dto.ts: -------------------------------------------------------------------------------- 1 | import { OmitType, PartialType } from '@nestjs/swagger'; 2 | 3 | import { CreateUserDto } from './create-user.dto'; 4 | 5 | export class UpdateUserDto extends PartialType( 6 | OmitType(CreateUserDto, ['password', 'socialId', 'socialProvider'] as const), 7 | ) {} 8 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/dtos/users/user.dto.ts: -------------------------------------------------------------------------------- 1 | import { AbstractDto, AbstractDtoOptions } from '@libs/server/util-common'; 2 | import { RoleType } from '@libs/shared/util-types'; 3 | import { ApiProperty } from '@nestjs/swagger'; 4 | 5 | import { UserOrmEntity } from '../../database/entities/user.orm-entity'; 6 | 7 | export type UserDtoOptions = AbstractDtoOptions; 8 | 9 | /** 10 | * UserDto represents data from the User entity _before_ serializing into JSON 11 | * for an API response. To serialize a User entity, use the User.toJSON() method 12 | */ 13 | export class UserDto extends AbstractDto { 14 | @ApiProperty() 15 | email!: string; 16 | 17 | @ApiProperty({ type: String }) 18 | firstName!: string | null; 19 | 20 | @ApiProperty({ type: String }) 21 | lastName!: string | null; 22 | 23 | @ApiProperty({ type: String }) 24 | avatar!: string | null; 25 | 26 | @ApiProperty({ enum: RoleType }) 27 | role!: RoleType; 28 | 29 | constructor(user: UserOrmEntity, opts?: UserDtoOptions) { 30 | super(user, opts); 31 | this.email = user.email; 32 | this.firstName = user.firstName; 33 | this.lastName = user.lastName; 34 | this.avatar = user.avatar; 35 | this.role = user.role; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /libs/server/data-access/src/lib/server-data-access.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { DatabaseModule } from './database/database.module'; 4 | 5 | @Module({ 6 | imports: [DatabaseModule], 7 | controllers: [], 8 | providers: [], 9 | exports: [DatabaseModule], 10 | }) 11 | export class ServerDataAccessModule {} 12 | -------------------------------------------------------------------------------- /libs/server/data-access/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/server/data-access/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"], 7 | "target": "es2021", 8 | "strictNullChecks": true, 9 | "noImplicitAny": true, 10 | "strictBindCallApply": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noFallthroughCasesInSwitch": true 13 | }, 14 | "include": ["src/**/*.ts"], 15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/data-access/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /libs/server/feat-auth/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/server/feat-auth/README.md: -------------------------------------------------------------------------------- 1 | # server-feat-auth 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test server-feat-auth` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/server/feat-auth/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'server-feat-auth', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/server/feat-auth', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/server/feat-auth/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-feat-auth", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/server/feat-auth/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/server/feat-auth/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 17 | "options": { 18 | "jestConfig": "libs/server/feat-auth/jest.config.ts", 19 | "passWithNoTests": true 20 | }, 21 | "configurations": { 22 | "ci": { 23 | "ci": true, 24 | "codeCoverage": true 25 | } 26 | } 27 | } 28 | }, 29 | "tags": ["type:feature", "scope:server"] 30 | } 31 | -------------------------------------------------------------------------------- /libs/server/feat-auth/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/server-feat-auth.controller'; 2 | export * from './lib/server-feat-auth.service'; 3 | export * from './lib/server-feat-auth.module'; 4 | -------------------------------------------------------------------------------- /libs/server/feat-auth/src/lib/google-auth/google-auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { SkipAuth } from '@libs/server/util-common'; 2 | import { AuthProviderType, ITokenResponse } from '@libs/shared/util-types'; 3 | import { Body, Controller, Post } from '@nestjs/common'; 4 | import { ApiTags } from '@nestjs/swagger'; 5 | 6 | import { ServerFeatAuthService } from '../server-feat-auth.service'; 7 | import { GoogleAuthService } from './google-auth.service'; 8 | import { GoogleAuthLoginDto } from './google-login.dto'; 9 | 10 | @Controller({ 11 | path: 'auth/google', 12 | version: '1', 13 | }) 14 | @ApiTags('Authentication') 15 | export class GoogleAuthController { 16 | constructor( 17 | private authService: ServerFeatAuthService, 18 | private googleService: GoogleAuthService, 19 | ) {} 20 | 21 | @Post('login') 22 | @SkipAuth() 23 | async login(@Body() dto: GoogleAuthLoginDto): Promise { 24 | const user = await this.googleService.getProfileByToken(dto); 25 | return await this.authService.validateSocialUser( 26 | AuthProviderType.GOOGLE, 27 | user, 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /libs/server/feat-auth/src/lib/google-auth/google-login.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString } from 'class-validator'; 2 | 3 | import { IGoogleLoginPayload } from '@libs/shared/util-types'; 4 | import { ApiProperty } from '@nestjs/swagger'; 5 | 6 | export class GoogleAuthLoginDto implements IGoogleLoginPayload { 7 | @ApiProperty() 8 | @IsString() 9 | idToken!: string; 10 | } 11 | -------------------------------------------------------------------------------- /libs/server/feat-auth/src/lib/server-feat-auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { ServerFeatUserService } from '@libs/server/feat-user'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import { JwtModule } from '@nestjs/jwt'; 4 | import { Test } from '@nestjs/testing'; 5 | import { randPassword } from '@ngneat/falso'; 6 | 7 | import { ServerFeatAuthService } from './server-feat-auth.service'; 8 | 9 | describe('ServerFeatAuthService', () => { 10 | let service: ServerFeatAuthService; 11 | 12 | beforeEach(async () => { 13 | const module = await Test.createTestingModule({ 14 | imports: [ 15 | JwtModule.register({ 16 | secret: randPassword(), 17 | }), 18 | ConfigModule.forRoot({ 19 | envFilePath: '.env.test', 20 | }), 21 | ], 22 | providers: [ 23 | ServerFeatAuthService, 24 | { 25 | provide: ServerFeatUserService, 26 | useValue: { 27 | createUser: jest.fn(() => null), 28 | }, 29 | }, 30 | ], 31 | }).compile(); 32 | 33 | service = module.get(ServerFeatAuthService); 34 | }); 35 | 36 | it('should be defined', () => { 37 | expect(service).toBeTruthy(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /libs/server/feat-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/server/feat-auth/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"], 7 | "target": "es2021", 8 | "strictNullChecks": true, 9 | "noImplicitAny": true, 10 | "strictBindCallApply": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noFallthroughCasesInSwitch": true 13 | }, 14 | "include": ["src/**/*.ts"], 15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/feat-auth/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /libs/server/feat-health/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/server/feat-health/README.md: -------------------------------------------------------------------------------- 1 | # server-feat-health 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test server-feat-health` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/server/feat-health/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'server-feat-health', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/server/feat-health', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/server/feat-health/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-feat-health", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/server/feat-health/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/server/feat-health/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 17 | "options": { 18 | "jestConfig": "libs/server/feat-health/jest.config.ts", 19 | "passWithNoTests": true 20 | }, 21 | "configurations": { 22 | "ci": { 23 | "ci": true, 24 | "codeCoverage": true 25 | } 26 | } 27 | } 28 | }, 29 | "tags": ["type:feature", "scope:server"] 30 | } 31 | -------------------------------------------------------------------------------- /libs/server/feat-health/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/server-feature-health.controller'; 2 | export * from './lib/server-feature-health.module'; 3 | -------------------------------------------------------------------------------- /libs/server/feat-health/src/lib/server-feature-health.controller.ts: -------------------------------------------------------------------------------- 1 | import { SkipAuth } from '@libs/server/util-common'; 2 | import { Controller, Get } from '@nestjs/common'; 3 | import { ApiTags } from '@nestjs/swagger'; 4 | import { 5 | HealthCheck, 6 | HealthCheckService, 7 | TypeOrmHealthIndicator, 8 | } from '@nestjs/terminus'; 9 | 10 | @Controller({ path: 'health', version: '1' }) 11 | @ApiTags('Health') 12 | export class ServerFeatureHealthController { 13 | constructor( 14 | private health: HealthCheckService, 15 | private db: TypeOrmHealthIndicator, 16 | ) {} 17 | 18 | @Get() 19 | @HealthCheck() 20 | @SkipAuth() 21 | check() { 22 | return this.health.check([() => this.db.pingCheck('database')]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /libs/server/feat-health/src/lib/server-feature-health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | 4 | import { ServerFeatureHealthController } from './server-feature-health.controller'; 5 | 6 | @Module({ 7 | imports: [TerminusModule], 8 | controllers: [ServerFeatureHealthController], 9 | providers: [], 10 | exports: [], 11 | }) 12 | export class ServerFeatureHealthModule {} 13 | -------------------------------------------------------------------------------- /libs/server/feat-health/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/server/feat-health/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"], 7 | "target": "es2021", 8 | "strictNullChecks": true, 9 | "noImplicitAny": true, 10 | "strictBindCallApply": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noFallthroughCasesInSwitch": true 13 | }, 14 | "include": ["src/**/*.ts"], 15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/feat-health/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /libs/server/feat-user/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/server/feat-user/README.md: -------------------------------------------------------------------------------- 1 | # server-feat-user 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test server-feat-user` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/server/feat-user/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'server-feat-user', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/server/feat-user', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/server/feat-user/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-feat-user", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/server/feat-user/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/server/feat-user/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 17 | "options": { 18 | "jestConfig": "libs/server/feat-user/jest.config.ts", 19 | "passWithNoTests": true 20 | }, 21 | "configurations": { 22 | "ci": { 23 | "ci": true, 24 | "codeCoverage": true 25 | } 26 | } 27 | } 28 | }, 29 | "tags": ["type:feature", "scope:server"] 30 | } 31 | -------------------------------------------------------------------------------- /libs/server/feat-user/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/server-feat-user.controller'; 2 | export * from './lib/server-feat-user.service'; 3 | export * from './lib/server-feat-user.module'; 4 | -------------------------------------------------------------------------------- /libs/server/feat-user/src/lib/server-feat-user.module.ts: -------------------------------------------------------------------------------- 1 | import { ServerDataAccessModule } from '@libs/server/data-access'; 2 | import { Module } from '@nestjs/common'; 3 | 4 | import { ServerFeatUserController } from './server-feat-user.controller'; 5 | import { ServerFeatUserService } from './server-feat-user.service'; 6 | 7 | @Module({ 8 | imports: [ServerDataAccessModule], 9 | controllers: [ServerFeatUserController], 10 | providers: [ServerFeatUserService], 11 | exports: [ServerFeatUserService], 12 | }) 13 | export class ServerFeatUserModule {} 14 | -------------------------------------------------------------------------------- /libs/server/feat-user/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/server/feat-user/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"], 7 | "target": "es2021", 8 | "strictNullChecks": true, 9 | "noImplicitAny": true, 10 | "strictBindCallApply": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noFallthroughCasesInSwitch": true 13 | }, 14 | "include": ["src/**/*.ts"], 15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/feat-user/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /libs/server/util-common/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/server/util-common/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-util-common", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/server/util-common/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/server/util-common/**/*.ts"] 12 | } 13 | } 14 | }, 15 | "tags": ["type:util", "scope:shared"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/util-common/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/database'; 2 | export * from './lib/decorators'; 3 | export * from './lib/dto'; 4 | export * from './lib/loggers'; 5 | export * from './lib/types'; 6 | export * from './lib/constants'; 7 | export * from './lib/filters'; 8 | export * from './lib/config'; 9 | export * from './lib/exceptions'; 10 | export * from './lib/guards'; 11 | export * from './lib/interceptors'; 12 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/config/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ENV_SERVER_HOST, ENV_SERVER_PORT } from '@libs/shared/util-constants'; 2 | import { registerAs } from '@nestjs/config'; 3 | 4 | export const appConfig = registerAs('app', () => ({ 5 | host: process.env[ENV_SERVER_HOST], 6 | port: process.env[ENV_SERVER_PORT], 7 | })); 8 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/config/database.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ENV_DATABASE_HOST, 3 | ENV_DATABASE_LOGGING_ENABLED, 4 | ENV_DATABASE_NAME, 5 | ENV_DATABASE_PASSWORD, 6 | ENV_DATABASE_PATH, 7 | ENV_DATABASE_PORT, 8 | ENV_DATABASE_SYNCHRONIZE, 9 | ENV_DATABASE_TYPE, 10 | ENV_DATABASE_USERNAME, 11 | } from '@libs/shared/util-constants'; 12 | import { registerAs } from '@nestjs/config'; 13 | 14 | export const dbConfig = registerAs('db', () => ({ 15 | type: process.env[ENV_DATABASE_TYPE], 16 | host: process.env[ENV_DATABASE_HOST], 17 | port: process.env[ENV_DATABASE_PORT], 18 | password: process.env[ENV_DATABASE_PASSWORD], 19 | name: process.env[ENV_DATABASE_NAME], 20 | path: process.env[ENV_DATABASE_PATH], 21 | username: process.env[ENV_DATABASE_USERNAME], 22 | synchronize: process.env[ENV_DATABASE_SYNCHRONIZE] === 'true', 23 | logging: process.env[ENV_DATABASE_LOGGING_ENABLED], 24 | })); 25 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/config/google.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ENV_SOCIAL_GOOGLE_CLIENT_ID, 3 | ENV_SOCIAL_GOOGLE_CLIENT_SECRET, 4 | ENV_SOCIAL_GOOGLE_ENABLED, 5 | } from '@libs/shared/util-constants'; 6 | import { registerAs } from '@nestjs/config'; 7 | 8 | export const googleConfig = registerAs('google', () => ({ 9 | enabled: process.env[ENV_SOCIAL_GOOGLE_ENABLED], 10 | clientId: process.env[ENV_SOCIAL_GOOGLE_CLIENT_ID], 11 | clientSecret: process.env[ENV_SOCIAL_GOOGLE_CLIENT_SECRET], 12 | })); 13 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './typeorm-config.service'; 2 | export * from './app.config'; 3 | export * from './database.config'; 4 | export * from './schema'; 5 | export * from './google.config'; 6 | export * from './mailer.config'; 7 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/config/typeorm-config.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { TypeormConfigService } from './typeorm-config.service'; 3 | 4 | describe('TypeormConfigService', () => { 5 | let service: TypeormConfigService; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | providers: [TypeormConfigService], 10 | }).compile(); 11 | 12 | service = module.get(TypeormConfigService); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/config/typeorm-config.service.ts: -------------------------------------------------------------------------------- 1 | import { ENV_ENVIRONMENT } from '@libs/shared/util-constants'; 2 | import { Injectable, Logger } from '@nestjs/common'; 3 | import { ConfigService } from '@nestjs/config'; 4 | import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm'; 5 | 6 | @Injectable() 7 | export class TypeormConfigService implements TypeOrmOptionsFactory { 8 | private logger = new Logger(TypeormConfigService.name); 9 | constructor(private configService: ConfigService) {} 10 | 11 | createTypeOrmOptions(): TypeOrmModuleOptions { 12 | const dbConfig = this.configService.get('db'); 13 | this.logger.debug( 14 | `Database Config for env ${this.configService.get( 15 | ENV_ENVIRONMENT, 16 | )}:\n\n${JSON.stringify(dbConfig, null, 2)}\n\n`, 17 | ); 18 | return { 19 | type: dbConfig.type, 20 | host: dbConfig.host, 21 | username: dbConfig.username, 22 | password: dbConfig.password, 23 | port: dbConfig.port, 24 | database: dbConfig.type === 'sqlite' ? dbConfig.path : dbConfig.name, 25 | synchronize: dbConfig.synchronize, 26 | logging: dbConfig.logging, 27 | autoLoadEntities: true, 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used as part of the `@SkipAuth()` decorator 3 | */ 4 | export const SKIP_AUTH_KEY = 'skipAuth'; 5 | 6 | /** 7 | * Used for the RoleGuard 8 | */ 9 | export const ROLES_REQUEST_KEY = 'roles'; 10 | 11 | export const STRATEGY_JWT_ACCESS = 'jwt-access'; 12 | export const STRATEGY_JWT_REFRESH = 'jwt-refresh'; 13 | 14 | export const CONFIG_NAMESPACE_MAILER = 'mailer'; 15 | export const CONFIG_NAMESPACE_APP = 'app'; 16 | export const CONFIG_NAMESPACE_DATABASE = 'db'; 17 | export const CONFIG_NAMESPACE_GOOGLE = 'google'; 18 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/database/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base.orm-entity'; 2 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/decorators/auth.decorator.ts: -------------------------------------------------------------------------------- 1 | import { applyDecorators, SetMetadata, UseGuards } from '@nestjs/common'; 2 | import { 3 | ApiBearerAuth, 4 | ApiForbiddenResponse, 5 | ApiInternalServerErrorResponse, 6 | ApiUnauthorizedResponse, 7 | } from '@nestjs/swagger'; 8 | 9 | import { RoleType } from '@libs/shared/util-types'; 10 | import { ROLES_REQUEST_KEY, SKIP_AUTH_KEY } from '../constants'; 11 | import { RolesGuard } from '../guards/role.guard'; 12 | import { JwtAccessTokenGuard } from '../guards/jwt-access.guard'; 13 | 14 | /** 15 | * Automatically apply certain Swagger decorators, auth decorators, and 16 | * token/role guards to any controller method. 17 | */ 18 | export function Auth( 19 | roles: RoleType[] = [], 20 | opts?: Partial<{ public: boolean }>, 21 | ): MethodDecorator { 22 | return applyDecorators( 23 | SetMetadata(ROLES_REQUEST_KEY, roles), 24 | SetMetadata(SKIP_AUTH_KEY, opts?.public), 25 | UseGuards(JwtAccessTokenGuard, RolesGuard), 26 | ApiBearerAuth(), 27 | ApiUnauthorizedResponse({ description: 'Unauthorized' }), 28 | ApiForbiddenResponse({ description: `Forbidden` }), 29 | ApiInternalServerErrorResponse({ description: 'Unknown server error' }), 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/decorators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-dto.decorator'; 2 | export * from './utc-date.decorator'; 3 | export * from './auth.decorator'; 4 | export * from './uuid-param.decorator'; 5 | export * from './req-user.decorator'; 6 | export * from './skip-auth.decorator'; 7 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/decorators/req-user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from '@nestjs/common'; 2 | 3 | /** 4 | * Returns all user data stored in the user object of a Request 5 | */ 6 | export const ReqUser = createParamDecorator( 7 | (data: unknown, ctx: ExecutionContext) => { 8 | const request = ctx.switchToHttp().getRequest(); 9 | // Logger.debug(JSON.stringify(request.user)); 10 | return request.user; 11 | }, 12 | ); 13 | 14 | /** 15 | * Returns the User ID stored in the user object of a Request 16 | * 17 | * @example getTodos(@ReqUserId() userId: string) {} 18 | */ 19 | export const ReqUserId = createParamDecorator( 20 | (data: unknown, ctx: ExecutionContext) => { 21 | const request = ctx.switchToHttp().getRequest(); 22 | return request.user.id as string; 23 | }, 24 | ); 25 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/decorators/skip-auth.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | 3 | import { SKIP_AUTH_KEY } from '../constants'; 4 | 5 | export const SkipAuth = () => SetMetadata(SKIP_AUTH_KEY, true); 6 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/decorators/use-dto.decorator.ts: -------------------------------------------------------------------------------- 1 | import { AbstractOrmEntity } from '../database'; 2 | import { AbstractDto } from '../dto/base.dto'; 3 | import { Constructor } from '../types'; 4 | 5 | export function UseDto( 6 | dtoClass: Constructor, 7 | ): ClassDecorator { 8 | return (ctor) => { 9 | ctor.prototype.dtoClass = dtoClass; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/decorators/uuid-param.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Param, ParseUUIDPipe, PipeTransform, Type } from '@nestjs/common'; 2 | 3 | export function UUIDParam( 4 | property: string, 5 | ...pipes: Array | PipeTransform> 6 | ): ParameterDecorator { 7 | return Param(property, new ParseUUIDPipe(), ...pipes); 8 | } 9 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/dto/base.dto.ts: -------------------------------------------------------------------------------- 1 | import { IBaseEntity } from '@libs/shared/util-types'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | 4 | import { AbstractOrmEntity } from '../database'; 5 | 6 | export type AbstractDtoOptions = Partial<{ excludeFields?: string[] }>; 7 | 8 | export class AbstractDto implements IBaseEntity { 9 | @ApiProperty() 10 | id!: string; 11 | 12 | @ApiProperty() 13 | createdAt!: Date; 14 | 15 | @ApiProperty() 16 | updatedAt!: Date; 17 | 18 | @ApiProperty({ 19 | type: Date, 20 | nullable: true, 21 | }) 22 | deletedAt!: Date | null; 23 | 24 | constructor(entity: AbstractOrmEntity, opts?: AbstractDtoOptions) { 25 | this.id = entity.id; 26 | this.createdAt = entity.createdAt; 27 | this.updatedAt = entity.updatedAt; 28 | if (opts) { 29 | // do stuff! 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base.dto'; 2 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/exceptions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user-not-found.exception'; 2 | export * from './token-expired.exception'; 3 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/exceptions/token-expired.exception.ts: -------------------------------------------------------------------------------- 1 | import { UnauthorizedException } from '@nestjs/common'; 2 | 3 | export class TokenExpiredException extends UnauthorizedException { 4 | constructor(msg?: string) { 5 | super('Access token expired', msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/exceptions/user-not-found.exception.ts: -------------------------------------------------------------------------------- 1 | import { NotFoundException } from '@nestjs/common'; 2 | 3 | export class UserNotFoundException extends NotFoundException { 4 | constructor(msg?: string) { 5 | super('User not found', msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/filters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './query-failed.filter'; 2 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/guards/index.ts: -------------------------------------------------------------------------------- 1 | export * from './jwt-access.guard'; 2 | export * from './jwt-refresh.guard'; 3 | export * from './role.guard'; 4 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/guards/jwt-access.guard.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { 3 | ExecutionContext, 4 | ForbiddenException, 5 | Injectable, 6 | UnauthorizedException, 7 | } from '@nestjs/common'; 8 | import { Reflector } from '@nestjs/core'; 9 | import { AuthGuard } from '@nestjs/passport'; 10 | 11 | import { SKIP_AUTH_KEY, STRATEGY_JWT_ACCESS } from '../constants'; 12 | import { TokenExpiredException } from '../exceptions'; 13 | 14 | @Injectable() 15 | export class JwtAccessTokenGuard extends AuthGuard(STRATEGY_JWT_ACCESS) { 16 | constructor(private reflector: Reflector) { 17 | super(); 18 | } 19 | 20 | override canActivate(ctx: ExecutionContext) { 21 | const skipAuth = this.reflector.getAllAndOverride(SKIP_AUTH_KEY, [ 22 | ctx.getHandler(), 23 | ctx.getClass(), 24 | ]); 25 | return skipAuth ?? super.canActivate(ctx); 26 | } 27 | 28 | override handleRequest(err: any, user: any, info: any) { 29 | if (info instanceof TokenExpiredException) { 30 | throw new ForbiddenException(); 31 | } 32 | if (err || !user) { 33 | throw err || new UnauthorizedException(); 34 | } 35 | return user; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/guards/role.guard.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; 2 | import { Reflector } from '@nestjs/core'; 3 | 4 | import { ROLES_REQUEST_KEY, SKIP_AUTH_KEY } from '../constants'; 5 | 6 | @Injectable() 7 | export class RolesGuard implements CanActivate { 8 | constructor(private reflector: Reflector) {} 9 | 10 | /** 11 | * FIXME currently throws 401 unauthorized if roles don't match, should throw Forbidden instead 12 | * @param context 13 | * @returns 14 | */ 15 | canActivate(context: ExecutionContext): boolean { 16 | const roles = this.reflector.getAllAndOverride(ROLES_REQUEST_KEY, [ 17 | context.getClass(), 18 | context.getHandler(), 19 | ]); 20 | const skipAuth = this.reflector.getAllAndOverride(SKIP_AUTH_KEY, [ 21 | context.getHandler(), 22 | context.getClass(), 23 | ]); 24 | if (!roles.length || skipAuth) { 25 | return true; 26 | } 27 | const request = context.switchToHttp().getRequest(); 28 | 29 | return roles.includes(request.user.role); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/interceptors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logging.interceptor'; 2 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/loggers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './database.logger'; 2 | -------------------------------------------------------------------------------- /libs/server/util-common/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type Constructor = new ( 2 | ...arguments_: Arguments 3 | ) => T; 4 | -------------------------------------------------------------------------------- /libs/server/util-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /libs/server/util-common/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/server/util-mailer/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/server/util-mailer/README.md: -------------------------------------------------------------------------------- 1 | # server-util-mailer 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test server-util-mailer` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/server/util-mailer/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'server-util-mailer', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/server/util-mailer', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/server/util-mailer/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-util-mailer", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/server/util-mailer/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/server/util-mailer/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 17 | "options": { 18 | "jestConfig": "libs/server/util-mailer/jest.config.ts", 19 | "passWithNoTests": true 20 | }, 21 | "configurations": { 22 | "ci": { 23 | "ci": true, 24 | "codeCoverage": true 25 | } 26 | } 27 | } 28 | }, 29 | "tags": ["type:util", "scope:server"] 30 | } 31 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/server-util-mailer.service'; 2 | export * from './lib/server-util-mailer.module'; 3 | export * from './templates/contexts'; 4 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/lib/mailer-config-options.interface.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/libs/server/util-mailer/src/lib/mailer-config-options.interface.ts -------------------------------------------------------------------------------- /libs/server/util-mailer/src/lib/server-util-mailer.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Global, Module } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | 4 | import { ServerUtilMailerService } from './server-util-mailer.service'; 5 | 6 | @Global() 7 | @Module({ 8 | imports: [ConfigModule], 9 | controllers: [], 10 | providers: [ServerUtilMailerService], 11 | exports: [ServerUtilMailerService], 12 | }) 13 | export class ServerUtilMailerModule { 14 | /** 15 | * NOTE: Potentially use `register()` to handle Mailer service configuration? 16 | */ 17 | static register(opts: Record): DynamicModule { 18 | if (opts) { 19 | // dynamic config? 20 | } 21 | return { 22 | module: ServerUtilMailerModule, 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/lib/server-util-mailer.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { mailerConfig } from '@libs/server/util-common'; 2 | import { Test } from '@nestjs/testing'; 3 | 4 | import { ServerUtilMailerService } from './server-util-mailer.service'; 5 | 6 | describe('ServerUtilMailerService', () => { 7 | let service: ServerUtilMailerService; 8 | 9 | beforeEach(async () => { 10 | const module = await Test.createTestingModule({ 11 | providers: [ 12 | ServerUtilMailerService, 13 | { 14 | provide: mailerConfig.KEY, 15 | useValue: {}, 16 | }, 17 | ], 18 | }).compile(); 19 | 20 | service = module.get(ServerUtilMailerService); 21 | }); 22 | 23 | it('should be defined', () => { 24 | expect(service).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/contexts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './layout.email-context'; 2 | export * from './verify-email.email-context'; 3 | export * from './reset-password.email-context'; 4 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/contexts/layout.email-context.ts: -------------------------------------------------------------------------------- 1 | export interface IBaseEmailContext { 2 | title: string; 3 | username: string; 4 | } 5 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/contexts/reset-password.email-context.ts: -------------------------------------------------------------------------------- 1 | import { IBaseEmailContext } from './layout.email-context'; 2 | 3 | export interface IResetPasswordEmailContext extends IBaseEmailContext { 4 | passwordResetLink: string; 5 | } 6 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/contexts/verify-email.email-context.ts: -------------------------------------------------------------------------------- 1 | import { IBaseEmailContext } from './layout.email-context'; 2 | 3 | export interface IVerifyEmailContext extends IBaseEmailContext { 4 | verificationLink: string; 5 | userEmailAddress: string; 6 | } 7 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/partials/footer.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/partials/header.hbs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wgd3/nx-nestjs-angular-boilerplate/2dd23ba5632ecd10e23853436ad44d86e1489e16/libs/server/util-mailer/src/templates/partials/header.hbs -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/reset-password.hbs: -------------------------------------------------------------------------------- 1 | {{#> layout }} 2 |

We have received your request to reset your password. Click the link below to get started:

3 | Reset Password 4 | {{/layout}} -------------------------------------------------------------------------------- /libs/server/util-mailer/src/templates/verify-email.hbs: -------------------------------------------------------------------------------- 1 | {{#> layout }} 2 |

Thanks for signing up! Please use the following link to verify your email:

3 | Verify {{ userEmailAddress }} 4 | {{/layout}} -------------------------------------------------------------------------------- /libs/server/util-mailer/src/testing/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mock-mailer.factory'; 2 | -------------------------------------------------------------------------------- /libs/server/util-mailer/src/testing/mock-mailer.factory.ts: -------------------------------------------------------------------------------- 1 | export const mockMailerFactory = () => jest.fn(() => ({})); 2 | -------------------------------------------------------------------------------- /libs/server/util-mailer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/server/util-mailer/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node", "jest"], 7 | "target": "es2021", 8 | "strictNullChecks": true, 9 | "noImplicitAny": true, 10 | "strictBindCallApply": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noFallthroughCasesInSwitch": true 13 | }, 14 | "include": ["src/**/*.ts"], 15 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/util-mailer/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /libs/server/util-testing/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/server/util-testing/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-util-testing", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/server/util-testing/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/server/util-testing/**/*.ts"] 12 | } 13 | } 14 | }, 15 | "tags": ["type:util", "scope:server"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/server/util-testing/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/mock-repo.factory'; 2 | -------------------------------------------------------------------------------- /libs/server/util-testing/src/lib/mock-repo.factory.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { ObjectLiteral, Repository } from 'typeorm'; 3 | 4 | export type MockType = { 5 | [P in keyof T]?: jest.Mock; 6 | }; 7 | 8 | export const mockRepoFactory: ( 9 | methodMocks?: Record unknown>, 10 | ) => MockType> = jest.fn((methodMocks) => ({ 11 | findOne: jest.fn((entity, ..._args: unknown[]) => entity), 12 | ...methodMocks, 13 | })); 14 | -------------------------------------------------------------------------------- /libs/server/util-testing/src/lib/server-util-testing.ts: -------------------------------------------------------------------------------- 1 | export function serverUtilTesting(): string { 2 | return 'server-util-testing'; 3 | } 4 | -------------------------------------------------------------------------------- /libs/server/util-testing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /libs/server/util-testing/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node", "jest"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'shared-ui-tailwind', 4 | preset: '../../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../../coverage/libs/shared/ui-tailwind', 11 | }; 12 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared-ui-tailwind", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/shared/ui-tailwind/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/shared/ui-tailwind/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 17 | "options": { 18 | "jestConfig": "libs/shared/ui-tailwind/jest.config.ts", 19 | "passWithNoTests": true 20 | }, 21 | "configurations": { 22 | "ci": { 23 | "ci": true, 24 | "codeCoverage": true 25 | } 26 | } 27 | } 28 | }, 29 | "tags": ["type:ui", "scope:shared"] 30 | } 31 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/src/index.ts: -------------------------------------------------------------------------------- 1 | export * as sharedTailwindConfig from './lib/tailwind.config'; 2 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/src/lib/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const defaultTheme = require('tailwindcss/defaultTheme'); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default module.exports = { 5 | theme: { 6 | ...defaultTheme, 7 | }, 8 | plugins: [require('@tailwindcss/forms')], 9 | }; 10 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts", "src/lib/tailwind.config.js"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared/ui-tailwind/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /libs/shared/util-constants/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/shared/util-constants/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared-util-constants", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/shared/util-constants/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/shared/util-constants/**/*.ts"] 12 | } 13 | } 14 | }, 15 | "tags": ["type:util", "scope:shared"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/shared/util-constants/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/env.constants'; 2 | export * from './lib/password.constants'; 3 | export * from './lib/pagination.contants'; 4 | -------------------------------------------------------------------------------- /libs/shared/util-constants/src/lib/pagination.contants.ts: -------------------------------------------------------------------------------- 1 | import { SortOrderType } from '@libs/shared/util-types'; 2 | 3 | export const PAGINATION_DEFAULT_PAGE = 1; 4 | export const PAGINATION_DEFAULT_PER_PAGE = 10; 5 | export const PAGINATION_DEFAULT_SKIP = 0; 6 | export const PAGINATION_DEFAULT_ORDER = SortOrderType.ASC; 7 | export const PAGINATION_DEFAULT_LIMIT = 100; 8 | -------------------------------------------------------------------------------- /libs/shared/util-constants/src/lib/password.constants.ts: -------------------------------------------------------------------------------- 1 | export const PASSWORD_MIN_LENGTH = 8; 2 | export const PASSWORD_MAX_LENGTH = 32; 3 | export const PASSWORD_MIN_UPPERCASE = 1; 4 | export const PASSWORD_MIN_SYMBOL = 1; 5 | export const PASSWORD_MIN_NUMBER = 1; 6 | -------------------------------------------------------------------------------- /libs/shared/util-constants/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /libs/shared/util-constants/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared/util-types/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/shared/util-types/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared-util-types", 3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/shared/util-types/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/shared/util-types/**/*.ts"] 12 | } 13 | } 14 | }, 15 | "tags": ["type:util", "scope:shared"] 16 | } 17 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/types'; 2 | export * from './lib/interfaces'; 3 | export * from './lib/enums'; 4 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/enums/auth-provider.enum.ts: -------------------------------------------------------------------------------- 1 | export enum AuthProviderType { 2 | EMAIL = 'email', 3 | GOOGLE = 'google', 4 | } 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/enums/index.ts: -------------------------------------------------------------------------------- 1 | export * from './role.enum'; 2 | export * from './sort-order.enum'; 3 | export * from './token-type.enum'; 4 | export * from './auth-provider.enum'; 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/enums/role.enum.ts: -------------------------------------------------------------------------------- 1 | export enum RoleType { 2 | USER = 'user', 3 | ADMIN = 'admin', 4 | } 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/enums/sort-order.enum.ts: -------------------------------------------------------------------------------- 1 | export enum SortOrderType { 2 | ASC = 'ASC', 3 | DESC = 'DESC', 4 | } 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/enums/token-type.enum.ts: -------------------------------------------------------------------------------- 1 | export enum TokenType { 2 | ACCESS_TOKEN = 'access_token', 3 | REFRESH_TOKEN = 'refresh_token', 4 | } 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/auth/forgot-password.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IForgotPasswordPayload { 2 | email: string; 3 | } 4 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/auth/google-login.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IGoogleLoginPayload { 2 | idToken: string; 3 | } 4 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './google-login.interface'; 2 | export * from './social-payload.interface'; 3 | export * from './forgot-password.interface'; 4 | export * from './reset-password.interface'; 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/auth/reset-password.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IResetPasswordPayload { 2 | email: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/auth/social-payload.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ISocialPayload { 2 | id: string; 3 | firstName?: string; 4 | lastName?: string; 5 | email?: string; 6 | avatar?: string; 7 | } 8 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/base-entity.interface.ts: -------------------------------------------------------------------------------- 1 | import { Uuid } from '../types'; 2 | 3 | export interface IBaseEntity { 4 | id: Uuid; 5 | createdAt: Date; 6 | updatedAt: Date; 7 | deletedAt: Date | null; 8 | } 9 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base-entity.interface'; 2 | export * from './user.interface'; 3 | export * from './user-login.interface'; 4 | export * from './token-response.interface'; 5 | export * from './jwt-payload.interface'; 6 | export * from './request-user.interface'; 7 | export * from './auth'; 8 | export * from './pagination'; 9 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/jwt-payload.interface.ts: -------------------------------------------------------------------------------- 1 | import { RoleType } from '../enums'; 2 | 3 | export interface IJwtPayload { 4 | /** 5 | * User UUID 6 | */ 7 | sub: string; 8 | 9 | email: string; 10 | 11 | role: RoleType; 12 | 13 | iat: string; 14 | 15 | exp: number; 16 | } 17 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/pagination/index.ts: -------------------------------------------------------------------------------- 1 | export * from './paginated-response.interface'; 2 | export * from './pagination-meta.interface'; 3 | export * from './pagination-options.interface'; 4 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/pagination/paginated-response.interface.ts: -------------------------------------------------------------------------------- 1 | import { IPaginationMeta } from './pagination-meta.interface'; 2 | 3 | export interface IPaginatedResponse { 4 | data: T[]; 5 | meta: IPaginationMeta; 6 | } 7 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/pagination/pagination-meta.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IPaginationMeta { 2 | page: number; 3 | perPage: number; 4 | totalItems: number; 5 | totalPages: number; 6 | hasNextPage: boolean; 7 | hasPreviousPage: boolean; 8 | nextPage: string | null; 9 | previousPage: string | null; 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/pagination/pagination-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { SortOrderType } from '../../enums'; 2 | 3 | export interface IPaginationOptions { 4 | order?: SortOrderType; 5 | page?: number; 6 | perPage?: number; 7 | limit?: number; 8 | skip?: number; 9 | } 10 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/request-user.interface.ts: -------------------------------------------------------------------------------- 1 | import { RoleType } from '../enums'; 2 | 3 | /** 4 | * Interface representing the `req.user` object created by Passport 5 | * when validating JWTs. Should only be needed by the backend. 6 | */ 7 | export interface IRequestUserData { 8 | /** 9 | * User UUID 10 | */ 11 | id: string; 12 | 13 | email: string; 14 | 15 | role: RoleType; 16 | } 17 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/token-response.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the API response when logging in with email/password 3 | */ 4 | export interface ITokenResponse { 5 | accessToken: string; 6 | refreshToken: string; 7 | } 8 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/interfaces/user-login.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IUserLogin { 2 | email: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /libs/shared/util-types/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Originally intended to be Value Objects, maybe implemented later 3 | */ 4 | export type Uuid = string; 5 | 6 | export type AccessToken = string; 7 | 8 | export type RefreshToken = string; 9 | -------------------------------------------------------------------------------- /libs/shared/util-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /libs/shared/util-types/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /nx-cloud.env.example: -------------------------------------------------------------------------------- 1 | NX_CLOUD_ACCESS_TOKEN=ZDdhMjZhN2MtMzQwOS00ZDNmLTkyODktMGI1NGQ0MTgyMzI5fHJlYWQtd3JpdGU= -------------------------------------------------------------------------------- /tools/tsconfig.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc/tools", 5 | "rootDir": ".", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["node"], 9 | "importHelpers": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | --------------------------------------------------------------------------------