├── server ├── .env ├── e2e │ ├── setup.test.js │ ├── jest.e2e.config.json │ └── app.e2e-spec.ts ├── .server.eslintignore ├── nest-cli.json ├── tsconfig.build.json ├── src │ ├── security │ │ ├── payload.interface.ts │ │ ├── role-type.ts │ │ ├── decorators │ │ │ ├── roles.decorator.ts │ │ │ └── auth-user.decorator.ts │ │ ├── index.ts │ │ ├── guards │ │ │ ├── auth.guard.ts │ │ │ └── roles.guard.ts │ │ ├── password-util.ts │ │ └── passport.jwt.strategy.ts │ ├── client │ │ ├── request.ts │ │ └── interceptors │ │ │ └── logging.interceptor.ts │ ├── repository │ │ ├── user.repository.ts │ │ └── authority.repository.ts │ ├── service │ │ ├── dto │ │ │ ├── base.dto.ts │ │ │ ├── password-change.dto.ts │ │ │ ├── user-login.dto.ts │ │ │ └── user.dto.ts │ │ └── mapper │ │ │ └── user.mapper.ts │ ├── domain │ │ ├── authority.entity.ts │ │ ├── base │ │ │ ├── base.entity.ts │ │ │ └── pagination.entity.ts │ │ └── user.entity.ts │ ├── module │ │ ├── user.module.ts │ │ └── auth.module.ts │ ├── migrations │ │ └── 1570200270081-CreateTables.ts │ ├── swagger.ts │ ├── web │ │ └── rest │ │ │ ├── management.controller.ts │ │ │ ├── user.jwt.controller.ts │ │ │ └── public.user.controller.ts │ ├── app.module.ts │ ├── config │ │ ├── application-dev.yml │ │ └── application-test.yml │ └── orm.config.ts ├── .prettierrc.js ├── sonar-project.properties ├── test │ └── admin │ │ └── management.controller.spec.ts ├── scripts │ └── copy-resources.ts └── webpack.server.prod.config.js ├── .huskyrc ├── src ├── main │ ├── webapp │ │ ├── app │ │ │ ├── config │ │ │ │ ├── pagination.constants.ts │ │ │ │ ├── authority.constants.ts │ │ │ │ ├── input.constants.ts │ │ │ │ ├── error.constants.ts │ │ │ │ ├── dayjs.ts │ │ │ │ ├── uib-pagination.config.ts │ │ │ │ ├── datepicker-adapter.ts │ │ │ │ └── font-awesome-icons.ts │ │ │ ├── layouts │ │ │ │ ├── footer │ │ │ │ │ ├── footer.component.html │ │ │ │ │ └── footer.component.ts │ │ │ │ ├── navbar │ │ │ │ │ ├── navbar.route.ts │ │ │ │ │ ├── navbar.component.scss │ │ │ │ │ └── navbar.component.ts │ │ │ │ ├── main │ │ │ │ │ ├── main.component.html │ │ │ │ │ └── main.component.ts │ │ │ │ ├── error │ │ │ │ │ ├── error.component.html │ │ │ │ │ ├── error.component.ts │ │ │ │ │ └── error.route.ts │ │ │ │ └── profiles │ │ │ │ │ ├── profile-info.model.ts │ │ │ │ │ ├── page-ribbon.component.ts │ │ │ │ │ ├── page-ribbon.component.scss │ │ │ │ │ └── profile.service.ts │ │ │ ├── shared │ │ │ │ ├── alert │ │ │ │ │ ├── alert-error.model.ts │ │ │ │ │ ├── alert.component.html │ │ │ │ │ ├── alert-error.component.html │ │ │ │ │ ├── alert.component.ts │ │ │ │ │ └── alert.component.spec.ts │ │ │ │ ├── date │ │ │ │ │ ├── duration.pipe.ts │ │ │ │ │ ├── format-medium-date.pipe.ts │ │ │ │ │ ├── format-medium-datetime.pipe.ts │ │ │ │ │ ├── format-medium-date.pipe.spec.ts │ │ │ │ │ └── format-medium-datetime.pipe.spec.ts │ │ │ │ ├── shared-libs.module.ts │ │ │ │ ├── sort │ │ │ │ │ ├── sort.directive.ts │ │ │ │ │ └── sort-by.directive.ts │ │ │ │ ├── pagination │ │ │ │ │ ├── item-count.component.ts │ │ │ │ │ └── item-count.component.spec.ts │ │ │ │ ├── shared.module.ts │ │ │ │ └── auth │ │ │ │ │ └── has-any-authority.directive.ts │ │ │ ├── login │ │ │ │ ├── login.model.ts │ │ │ │ ├── login.route.ts │ │ │ │ ├── login.module.ts │ │ │ │ ├── login.service.ts │ │ │ │ └── login.component.ts │ │ │ ├── admin │ │ │ │ ├── docs │ │ │ │ │ ├── docs.component.scss │ │ │ │ │ ├── docs.component.html │ │ │ │ │ ├── docs.component.ts │ │ │ │ │ ├── docs.route.ts │ │ │ │ │ └── docs.module.ts │ │ │ │ ├── logs │ │ │ │ │ ├── logs.route.ts │ │ │ │ │ ├── log.model.ts │ │ │ │ │ ├── logs.module.ts │ │ │ │ │ ├── logs.service.ts │ │ │ │ │ ├── logs.service.spec.ts │ │ │ │ │ └── logs.component.ts │ │ │ │ ├── health │ │ │ │ │ ├── health.route.ts │ │ │ │ │ ├── health.model.ts │ │ │ │ │ ├── health.module.ts │ │ │ │ │ ├── health.service.ts │ │ │ │ │ ├── modal │ │ │ │ │ │ ├── health-modal.component.ts │ │ │ │ │ │ └── health-modal.component.html │ │ │ │ │ ├── health.component.ts │ │ │ │ │ └── health.component.html │ │ │ │ ├── metrics │ │ │ │ │ ├── metrics.route.ts │ │ │ │ │ ├── blocks │ │ │ │ │ │ ├── jvm-memory │ │ │ │ │ │ │ ├── jvm-memory.component.ts │ │ │ │ │ │ │ └── jvm-memory.component.html │ │ │ │ │ │ ├── metrics-endpoints-requests │ │ │ │ │ │ │ ├── metrics-endpoints-requests.component.ts │ │ │ │ │ │ │ └── metrics-endpoints-requests.component.html │ │ │ │ │ │ ├── metrics-garbagecollector │ │ │ │ │ │ │ └── metrics-garbagecollector.component.ts │ │ │ │ │ │ ├── metrics-cache │ │ │ │ │ │ │ ├── metrics-cache.component.ts │ │ │ │ │ │ │ └── metrics-cache.component.html │ │ │ │ │ │ ├── metrics-datasource │ │ │ │ │ │ │ └── metrics-datasource.component.ts │ │ │ │ │ │ ├── metrics-request │ │ │ │ │ │ │ ├── metrics-request.component.ts │ │ │ │ │ │ │ └── metrics-request.component.html │ │ │ │ │ │ ├── metrics-system │ │ │ │ │ │ │ └── metrics-system.component.ts │ │ │ │ │ │ ├── jvm-threads │ │ │ │ │ │ │ ├── jvm-threads.component.ts │ │ │ │ │ │ │ └── jvm-threads.component.html │ │ │ │ │ │ └── metrics-modal-threads │ │ │ │ │ │ │ └── metrics-modal-threads.component.ts │ │ │ │ │ ├── metrics.service.ts │ │ │ │ │ ├── metrics.component.ts │ │ │ │ │ ├── metrics.module.ts │ │ │ │ │ ├── metrics.component.spec.ts │ │ │ │ │ └── metrics.component.html │ │ │ │ ├── configuration │ │ │ │ │ ├── configuration.route.ts │ │ │ │ │ ├── configuration.module.ts │ │ │ │ │ ├── configuration.model.ts │ │ │ │ │ ├── configuration.service.ts │ │ │ │ │ └── configuration.component.ts │ │ │ │ ├── user-management │ │ │ │ │ ├── detail │ │ │ │ │ │ ├── user-management-detail.component.ts │ │ │ │ │ │ ├── user-management-detail.component.html │ │ │ │ │ │ └── user-management-detail.component.spec.ts │ │ │ │ │ ├── delete │ │ │ │ │ │ ├── user-management-delete-dialog.component.ts │ │ │ │ │ │ ├── user-management-delete-dialog.component.html │ │ │ │ │ │ └── user-management-delete-dialog.component.spec.ts │ │ │ │ │ ├── user-management.model.ts │ │ │ │ │ ├── user-management.module.ts │ │ │ │ │ ├── service │ │ │ │ │ │ └── user-management.service.ts │ │ │ │ │ └── user-management.route.ts │ │ │ │ └── admin-routing.module.ts │ │ │ ├── account │ │ │ │ ├── register │ │ │ │ │ ├── register.model.ts │ │ │ │ │ ├── register.route.ts │ │ │ │ │ └── register.service.ts │ │ │ │ ├── activate │ │ │ │ │ ├── activate.route.ts │ │ │ │ │ ├── activate.component.html │ │ │ │ │ ├── activate.service.ts │ │ │ │ │ └── activate.component.ts │ │ │ │ ├── password │ │ │ │ │ ├── password-strength-bar │ │ │ │ │ │ ├── password-strength-bar.component.html │ │ │ │ │ │ └── password-strength-bar.component.scss │ │ │ │ │ ├── password.route.ts │ │ │ │ │ ├── password.service.ts │ │ │ │ │ └── password.component.ts │ │ │ │ ├── password-reset │ │ │ │ │ ├── init │ │ │ │ │ │ ├── password-reset-init.route.ts │ │ │ │ │ │ ├── password-reset-init.service.ts │ │ │ │ │ │ └── password-reset-init.component.ts │ │ │ │ │ └── finish │ │ │ │ │ │ ├── password-reset-finish.route.ts │ │ │ │ │ │ ├── password-reset-finish.service.ts │ │ │ │ │ │ └── password-reset-finish.component.ts │ │ │ │ ├── settings │ │ │ │ │ ├── settings.route.ts │ │ │ │ │ └── settings.component.ts │ │ │ │ ├── account.route.ts │ │ │ │ └── account.module.ts │ │ │ ├── core │ │ │ │ ├── request │ │ │ │ │ ├── request.model.ts │ │ │ │ │ └── request-util.ts │ │ │ │ ├── util │ │ │ │ │ ├── operators.spec.ts │ │ │ │ │ ├── operators.ts │ │ │ │ │ ├── data-util.service.spec.ts │ │ │ │ │ ├── parse-links.service.spec.ts │ │ │ │ │ ├── parse-links.service.ts │ │ │ │ │ └── event-manager.service.ts │ │ │ │ ├── auth │ │ │ │ │ ├── account.model.ts │ │ │ │ │ ├── state-storage.service.ts │ │ │ │ │ ├── user-route-access.service.ts │ │ │ │ │ └── auth-jwt.service.ts │ │ │ │ ├── config │ │ │ │ │ ├── application-config.service.ts │ │ │ │ │ └── application-config.service.spec.ts │ │ │ │ └── interceptor │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── error-handler.interceptor.ts │ │ │ │ │ ├── notification.interceptor.ts │ │ │ │ │ ├── auth.interceptor.ts │ │ │ │ │ └── auth-expired.interceptor.ts │ │ │ ├── home │ │ │ │ ├── home.route.ts │ │ │ │ ├── home.module.ts │ │ │ │ ├── home.component.scss │ │ │ │ ├── home.component.ts │ │ │ │ └── home.component.spec.ts │ │ │ ├── entities │ │ │ │ ├── user │ │ │ │ │ ├── user.model.ts │ │ │ │ │ └── user.service.ts │ │ │ │ └── entity-routing.module.ts │ │ │ ├── app.constants.ts │ │ │ └── app-routing.module.ts │ │ ├── favicon.ico │ │ ├── content │ │ │ ├── images │ │ │ │ ├── logo-jhipster.png │ │ │ │ ├── jhipster_family_member_0_head-192.png │ │ │ │ ├── jhipster_family_member_0_head-256.png │ │ │ │ ├── jhipster_family_member_0_head-384.png │ │ │ │ ├── jhipster_family_member_0_head-512.png │ │ │ │ ├── jhipster_family_member_1_head-192.png │ │ │ │ ├── jhipster_family_member_1_head-256.png │ │ │ │ ├── jhipster_family_member_1_head-384.png │ │ │ │ ├── jhipster_family_member_1_head-512.png │ │ │ │ ├── jhipster_family_member_2_head-192.png │ │ │ │ ├── jhipster_family_member_2_head-256.png │ │ │ │ ├── jhipster_family_member_2_head-384.png │ │ │ │ ├── jhipster_family_member_2_head-512.png │ │ │ │ ├── jhipster_family_member_3_head-192.png │ │ │ │ ├── jhipster_family_member_3_head-256.png │ │ │ │ ├── jhipster_family_member_3_head-384.png │ │ │ │ └── jhipster_family_member_3_head-512.png │ │ │ └── scss │ │ │ │ ├── vendor.scss │ │ │ │ └── _bootstrap-variables.scss │ │ ├── swagger-ui │ │ │ └── dist │ │ │ │ └── images │ │ │ │ └── throbber.gif │ │ ├── polyfills.ts │ │ ├── robots.txt │ │ ├── WEB-INF │ │ │ └── web.xml │ │ ├── main.ts │ │ ├── manifest.webapp │ │ └── 404.html │ └── docker │ │ ├── app.yml │ │ ├── sonar.yml │ │ └── mysql.yml └── test │ └── javascript │ └── protractor.conf.js ├── .prettierignore ├── webpack ├── logo-jhipster.png └── proxy.conf.js ├── tsconfig.e2e.json ├── .lintstagedrc.js ├── .dockerignore ├── tsconfig.app.json ├── .eslintignore ├── .prettierrc ├── tsconfig.spec.json ├── Dockerfile ├── .editorconfig ├── ngsw-config.json ├── tsconfig.json ├── .browserslistrc ├── jest.conf.js ├── .yo-rc.json └── sonar-project.properties /server/.env: -------------------------------------------------------------------------------- 1 | BACKEND_ENV=dev 2 | -------------------------------------------------------------------------------- /server/e2e/setup.test.js: -------------------------------------------------------------------------------- 1 | process.env.BACKEND_ENV = 'test'; 2 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/pagination.constants.ts: -------------------------------------------------------------------------------- 1 | export const ITEMS_PER_PAGE = 20; 2 | -------------------------------------------------------------------------------- /server/.server.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist 3 | coverage 4 | scripts 5 | webpack.server.prod.config.js 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | target 3 | build 4 | package-lock.json 5 | .git 6 | .mvn 7 | gradle 8 | .gradle 9 | -------------------------------------------------------------------------------- /webpack/logo-jhipster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/webpack/logo-jhipster.png -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /server/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/authority.constants.ts: -------------------------------------------------------------------------------- 1 | export enum Authority { 2 | ADMIN = 'ROLE_ADMIN', 3 | USER = 'ROLE_USER', 4 | } 5 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert-error.model.ts: -------------------------------------------------------------------------------- 1 | export class AlertError { 2 | constructor(public message: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '{,src/**/,webpack/}*.{md,json,yml,html,js,ts,tsx,css,scss,java}': ['prettier --write'], 3 | }; 4 | -------------------------------------------------------------------------------- /server/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/input.constants.ts: -------------------------------------------------------------------------------- 1 | export const DATE_FORMAT = 'YYYY-MM-DD'; 2 | export const DATE_TIME_FORMAT = 'YYYY-MM-DDTHH:mm'; 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | server/node_modules 3 | package-lock.json 4 | server/package-lock.json 5 | yarn.lock 6 | server/yarn.lock 7 | target 8 | -------------------------------------------------------------------------------- /server/src/security/payload.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Payload { 2 | id: number; 3 | username: string; 4 | authorities?: string[]; 5 | } 6 | -------------------------------------------------------------------------------- /server/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: "all", 4 | singleQuote: true, 5 | printWidth: 120, 6 | tabWidth: 4 7 | }; 8 | -------------------------------------------------------------------------------- /src/main/webapp/content/images/logo-jhipster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/logo-jhipster.png -------------------------------------------------------------------------------- /src/main/webapp/app/login/login.model.ts: -------------------------------------------------------------------------------- 1 | export class Login { 2 | constructor(public username: string, public password: string, public rememberMe: boolean) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/main/webapp/swagger-ui/dist/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/swagger-ui/dist/images/throbber.gif -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.component.scss: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/scss/functions'; 2 | @import '~bootstrap/scss/variables'; 3 | 4 | iframe { 5 | background: white; 6 | } 7 | -------------------------------------------------------------------------------- /server/src/security/role-type.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export enum RoleType { 4 | USER = 'ROLE_USER', 5 | ADMIN = 'ROLE_ADMIN', 6 | ANONYMOUS = 'ROLE_ANONYMOUS', 7 | } 8 | -------------------------------------------------------------------------------- /src/main/docker/app.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | gen-app: 4 | build: ../../.. 5 | container_name: gen-app 6 | environment: 7 | ports: 8 | - 8081:8081 9 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "files": ["src/main/webapp/main.ts", "src/main/webapp/polyfills.ts"], 4 | "include": ["src/main/webapp/**/*.d.ts"] 5 | } 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | src/main/docker/ 3 | src/test/javascript/protractor.conf.js 4 | jest.conf.js 5 | webpack/ 6 | target/ 7 | build/ 8 | node/ 9 | postcss.config.js 10 | server 11 | -------------------------------------------------------------------------------- /src/main/docker/sonar.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | gen-sonar: 4 | image: sonarqube:8.8-community 5 | container_name: sonar 6 | ports: 7 | - 9001:9000 8 | - 9092:9092 9 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/register/register.model.ts: -------------------------------------------------------------------------------- 1 | export class Registration { 2 | constructor(public login: string, public email: string, public password: string, public langKey: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_0_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_0_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_0_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_0_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_1_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_1_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_1_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_1_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_2_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_2_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_2_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_2_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_3_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_3_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_3_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-nodejs/HEAD/src/main/webapp/content/images/jhipster_family_member_3_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone'; 2 | import '@angular/localize/init'; 3 | 4 | // Fix needed for SockJS, see https://github.com/sockjs/sockjs-client/issues/439 5 | (window as any).global = window; 6 | -------------------------------------------------------------------------------- /server/src/client/request.ts: -------------------------------------------------------------------------------- 1 | import { Request as ExpressRequest } from 'express'; 2 | import { UserDTO } from '../service/dto/user.dto'; 3 | 4 | export interface Request extends ExpressRequest { 5 | user?: UserDTO; 6 | } 7 | -------------------------------------------------------------------------------- /server/src/security/decorators/roles.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | import { RoleType } from '../role-type'; 3 | 4 | export const Roles = (...roles: RoleType[]): any => SetMetadata('roles', roles); 5 | -------------------------------------------------------------------------------- /server/src/repository/user.repository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from 'typeorm'; 2 | import { User } from '../domain/user.entity'; 3 | 4 | @EntityRepository(User) 5 | export class UserRepository extends Repository {} 6 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'jhi-footer', 5 | templateUrl: './footer.component.html', 6 | }) 7 | export class FooterComponent {} 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.component.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /server/sonar-project.properties: -------------------------------------------------------------------------------- 1 | # --- properties for sonar --- 2 | sonar.projectKey=gen-nodejs 3 | sonar.projectVersion=0.0.1 4 | sonar.host.url=http://localhost:9001 5 | sonar.sources=src 6 | sonar.tests=e2e 7 | sonar.typescript.lcov.reportPaths=coverage/lcov.info 8 | -------------------------------------------------------------------------------- /server/src/repository/authority.repository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from 'typeorm'; 2 | import { Authority } from '../domain/authority.entity'; 3 | 4 | @EntityRepository(Authority) 5 | export class AuthorityRepository extends Repository {} 6 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'jhi-docs', 5 | templateUrl: './docs.component.html', 6 | styleUrls: ['./docs.component.scss'], 7 | }) 8 | export class DocsComponent {} 9 | -------------------------------------------------------------------------------- /server/src/security/index.ts: -------------------------------------------------------------------------------- 1 | export * from './guards/auth.guard'; 2 | export * from './guards/roles.guard'; 3 | export * from './decorators/roles.decorator'; 4 | export * from './decorators/auth-user.decorator'; 5 | export * from './password-util'; 6 | export * from './role-type'; 7 | -------------------------------------------------------------------------------- /server/src/service/dto/base.dto.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A DTO base object. 3 | */ 4 | export class BaseDTO { 5 | id?: number; 6 | 7 | createdBy?: string; 8 | 9 | createdDate?: Date; 10 | 11 | lastModifiedBy?: string; 12 | 13 | lastModifiedDate?: Date; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/error.constants.ts: -------------------------------------------------------------------------------- 1 | export const PROBLEM_BASE_URL = 'https://www.jhipster.tech/problem'; 2 | export const EMAIL_ALREADY_USED_TYPE = PROBLEM_BASE_URL + '/email-already-used'; 3 | export const LOGIN_ALREADY_USED_TYPE = PROBLEM_BASE_URL + '/login-already-used'; 4 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/navbar/navbar.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { NavbarComponent } from './navbar.component'; 4 | 5 | export const navbarRoute: Route = { 6 | path: '', 7 | component: NavbarComponent, 8 | outlet: 'navbar', 9 | }; 10 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/request/request.model.ts: -------------------------------------------------------------------------------- 1 | export interface Pagination { 2 | page: number; 3 | size: number; 4 | sort: string[]; 5 | } 6 | 7 | export interface Search { 8 | query: string; 9 | } 10 | 11 | export interface SearchWithPagination extends Search, Pagination {} 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { DocsComponent } from './docs.component'; 4 | 5 | export const docsRoute: Route = { 6 | path: '', 7 | component: DocsComponent, 8 | data: { 9 | pageTitle: 'API', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/logs.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { LogsComponent } from './logs.component'; 4 | 5 | export const logsRoute: Route = { 6 | path: '', 7 | component: LogsComponent, 8 | data: { 9 | pageTitle: 'Logs', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/login/login.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | import { LoginComponent } from './login.component'; 3 | 4 | export const LOGIN_ROUTE: Route = { 5 | path: '', 6 | component: LoginComponent, 7 | data: { 8 | pageTitle: 'Sign in', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /server/src/security/decorators/auth-user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from '@nestjs/common'; 2 | 3 | export const AuthUser = createParamDecorator((data: unknown, ctx: ExecutionContext) => { 4 | const request = ctx.switchToHttp().getRequest(); 5 | return request.user; 6 | }); 7 | -------------------------------------------------------------------------------- /src/main/webapp/app/home/home.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | export const HOME_ROUTE: Route = { 6 | path: '', 7 | component: HomeComponent, 8 | data: { 9 | pageTitle: 'Welcome, Java Hipster!', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | Disallow: /api/account 5 | Disallow: /api/account/change-password 6 | Disallow: /api/account/sessions 7 | Disallow: /api/logs/ 8 | Disallow: /api/users/ 9 | Disallow: /management/ 10 | Disallow: /v2/api-docs/ 11 | Disallow: /v3/api-docs/ 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { HealthComponent } from './health.component'; 4 | 5 | export const healthRoute: Route = { 6 | path: '', 7 | component: HealthComponent, 8 | data: { 9 | pageTitle: 'Health Checks', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/metrics.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { MetricsComponent } from './metrics.component'; 4 | 5 | export const metricsRoute: Route = { 6 | path: '', 7 | component: MetricsComponent, 8 | data: { 9 | pageTitle: 'Application Metrics', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert.component.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { ActivateComponent } from './activate.component'; 4 | 5 | export const activateRoute: Route = { 6 | path: 'activate', 7 | component: ActivateComponent, 8 | data: { 9 | pageTitle: 'Activation', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/operators.spec.ts: -------------------------------------------------------------------------------- 1 | import { isPresent } from './operators'; 2 | 3 | describe('Operators Test', () => { 4 | describe('isPresent', () => { 5 | it('should remove null and undefined values', () => { 6 | expect([1, null, undefined].filter(isPresent)).toEqual([1]); 7 | }); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | # Prettier configuration 2 | 3 | printWidth: 140 4 | singleQuote: true 5 | tabWidth: 2 6 | useTabs: false 7 | 8 | # js and ts rules: 9 | arrowParens: avoid 10 | 11 | # jsx and tsx rules: 12 | jsxBracketSameLine: false 13 | 14 | # java rules: 15 | overrides: 16 | - files: "*.java" 17 | options: 18 | tabWidth: 4 19 | -------------------------------------------------------------------------------- /server/src/domain/authority.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryColumn } from 'typeorm'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | 4 | @Entity('nhi_authority') 5 | export class Authority { 6 | @ApiModelProperty({ example: 'ROLE_USER', description: 'User role' }) 7 | @PrimaryColumn() 8 | name: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/register/register.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { RegisterComponent } from './register.component'; 4 | 5 | export const registerRoute: Route = { 6 | path: 'register', 7 | component: RegisterComponent, 8 | data: { 9 | pageTitle: 'Registration', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/operators.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Function used to workaround https://github.com/microsoft/TypeScript/issues/16069 3 | * es2019 alternative `const filteredArr = myArr.flatMap((x) => x ? x : []);` 4 | */ 5 | export function isPresent(t: T | undefined | null | void): t is T { 6 | return t !== undefined && t !== null; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert-error.component.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/user/user.model.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | id?: number; 3 | login?: string; 4 | } 5 | 6 | export class User implements IUser { 7 | constructor(public id: number, public login: string) {} 8 | } 9 | 10 | export function getUserIdentifier(user: IUser): number | undefined { 11 | return user.id; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/main/main.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 |
8 |
9 | 10 |
11 | 12 | 13 |
14 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDecoratorMetadata": true, 5 | "outDir": "target/out-tsc/spec", 6 | "types": ["jest", "node"] 7 | }, 8 | "files": ["src/main/webapp/polyfills.ts"], 9 | "include": ["src/main/webapp/**/*.spec.ts", "src/main/webapp/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/configuration/configuration.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { ConfigurationComponent } from './configuration.component'; 4 | 5 | export const configurationRoute: Route = { 6 | path: '', 7 | component: ConfigurationComponent, 8 | data: { 9 | pageTitle: 'Configuration', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.html: -------------------------------------------------------------------------------- 1 |
2 | Password strength: 3 |
    4 |
  • 5 |
  • 6 |
  • 7 |
  • 8 |
  • 9 |
10 |
11 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/entity-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | @NgModule({ 5 | imports: [ 6 | RouterModule.forChild([ 7 | /* jhipster-needle-add-entity-route - JHipster will add entity modules routes here */ 8 | ]), 9 | ], 10 | }) 11 | export class EntityRoutingModule {} 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14-alpine 2 | 3 | # Create app directory 4 | 5 | WORKDIR /usr/node-app 6 | 7 | ENV NODE_SERVER_PORT=8081 8 | 9 | COPY . . 10 | 11 | # install server deps 12 | 13 | WORKDIR server 14 | 15 | RUN npm install 16 | 17 | # install client deps 18 | 19 | WORKDIR .. 20 | 21 | RUN npm install 22 | 23 | EXPOSE 8081 24 | 25 | ENTRYPOINT ["npm", "run", "start:app" ] 26 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/auth/account.model.ts: -------------------------------------------------------------------------------- 1 | export class Account { 2 | constructor( 3 | public activated: boolean, 4 | public authorities: string[], 5 | public email: string, 6 | public firstName: string | null, 7 | public langKey: string, 8 | public lastName: string | null, 9 | public login: string, 10 | public imageUrl: string | null 11 | ) {} 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { PasswordResetInitComponent } from './password-reset-init.component'; 4 | 5 | export const passwordResetInitRoute: Route = { 6 | path: 'reset/request', 7 | component: PasswordResetInitComponent, 8 | data: { 9 | pageTitle: 'Password', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /server/e2e/jest.e2e.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": "..", 4 | "testEnvironment": "node", 5 | "testRegex": "(/e2e/.*|\\.(e2e-spec))\\.(ts)$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | }, 9 | "coverageDirectory": "coverage", 10 | "setupFiles": ["./e2e/setup.test.js"], 11 | "testTimeout": 10000 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { PasswordResetFinishComponent } from './password-reset-finish.component'; 4 | 5 | export const passwordResetFinishRoute: Route = { 6 | path: 'reset/finish', 7 | component: PasswordResetFinishComponent, 8 | data: { 9 | pageTitle: 'Password', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/duration.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import * as dayjs from 'dayjs'; 4 | 5 | @Pipe({ 6 | name: 'duration', 7 | }) 8 | export class DurationPipe implements PipeTransform { 9 | transform(value: any): string { 10 | if (value) { 11 | return dayjs.duration(value).humanize(); 12 | } 13 | return ''; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-date.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import * as dayjs from 'dayjs'; 4 | 5 | @Pipe({ 6 | name: 'formatMediumDate', 7 | }) 8 | export class FormatMediumDatePipe implements PipeTransform { 9 | transform(day: dayjs.Dayjs | null | undefined): string { 10 | return day ? day.format('D MMM YYYY') : ''; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/src/security/guards/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionContext, Injectable, Logger } from '@nestjs/common'; 2 | import { AuthGuard as NestAuthGuard } from '@nestjs/passport'; 3 | 4 | @Injectable() 5 | export class AuthGuard extends NestAuthGuard('jwt') { 6 | logger = new Logger('authGuard'); 7 | 8 | canActivate(context: ExecutionContext): any { 9 | return super.canActivate(context); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/error/error.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 |
8 |

Error Page!

9 | 10 |
11 |
{{ errorMessage }}
12 |
13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-datetime.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import * as dayjs from 'dayjs'; 4 | 5 | @Pipe({ 6 | name: 'formatMediumDatetime', 7 | }) 8 | export class FormatMediumDatetimePipe implements PipeTransform { 9 | transform(day: dayjs.Dayjs | null | undefined): string { 10 | return day ? day.format('D MMM YYYY HH:mm:ss') : ''; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/log.model.ts: -------------------------------------------------------------------------------- 1 | export type Level = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'OFF'; 2 | 3 | export interface Logger { 4 | configuredLevel: Level | null; 5 | effectiveLevel: Level; 6 | } 7 | 8 | export interface LoggersResponse { 9 | levels: Level[]; 10 | loggers: { [key: string]: Logger }; 11 | } 12 | 13 | export class Log { 14 | constructor(public name: string, public level: Level) {} 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/profile-info.model.ts: -------------------------------------------------------------------------------- 1 | export interface InfoResponse { 2 | 'display-ribbon-on-profiles'?: string; 3 | git?: any; 4 | build?: any; 5 | activeProfiles?: string[]; 6 | } 7 | 8 | export class ProfileInfo { 9 | constructor( 10 | public activeProfiles?: string[], 11 | public ribbonEnv?: string, 12 | public inProduction?: boolean, 13 | public openAPIEnabled?: boolean 14 | ) {} 15 | } 16 | -------------------------------------------------------------------------------- /server/src/security/password-util.ts: -------------------------------------------------------------------------------- 1 | import * as bcrypt from 'bcrypt'; 2 | import { config } from '../config'; 3 | 4 | export async function transformPassword(user: { password?: string }): Promise { 5 | if (user.password) { 6 | user.password = await bcrypt.hash( 7 | user.password, 8 | config.get('jhipster.security.authentication.jwt.hash-salt-or-rounds'), 9 | ); 10 | } 11 | return Promise.resolve(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 4 | import { PasswordComponent } from './password.component'; 5 | 6 | export const passwordRoute: Route = { 7 | path: 'password', 8 | component: PasswordComponent, 9 | data: { 10 | pageTitle: 'Password', 11 | }, 12 | canActivate: [UserRouteAccessService], 13 | }; 14 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/settings/settings.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 4 | import { SettingsComponent } from './settings.component'; 5 | 6 | export const settingsRoute: Route = { 7 | path: 'settings', 8 | component: SettingsComponent, 9 | data: { 10 | pageTitle: 'Settings', 11 | }, 12 | canActivate: [UserRouteAccessService], 13 | }; 14 | -------------------------------------------------------------------------------- /src/main/webapp/app/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import { SharedModule } from 'app/shared/shared.module'; 5 | import { HOME_ROUTE } from './home.route'; 6 | import { HomeComponent } from './home.component'; 7 | 8 | @NgModule({ 9 | imports: [SharedModule, RouterModule.forChild([HOME_ROUTE])], 10 | declarations: [HomeComponent], 11 | }) 12 | export class HomeModule {} 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { SharedModule } from 'app/shared/shared.module'; 4 | 5 | import { DocsComponent } from './docs.component'; 6 | import { docsRoute } from './docs.route'; 7 | 8 | @NgModule({ 9 | imports: [SharedModule, RouterModule.forChild([docsRoute])], 10 | declarations: [DocsComponent], 11 | }) 12 | export class DocsModule {} 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/logs.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { SharedModule } from 'app/shared/shared.module'; 4 | 5 | import { LogsComponent } from './logs.component'; 6 | import { logsRoute } from './logs.route'; 7 | 8 | @NgModule({ 9 | imports: [SharedModule, RouterModule.forChild([logsRoute])], 10 | declarations: [LogsComponent], 11 | }) 12 | export class LogsModule {} 13 | -------------------------------------------------------------------------------- /src/main/webapp/content/scss/vendor.scss: -------------------------------------------------------------------------------- 1 | /* after changing this file run 'npm run webapp:build' */ 2 | 3 | /*************************** 4 | put Sass variables here: 5 | eg $input-color: red; 6 | ****************************/ 7 | // Override Bootstrap variables 8 | @import 'bootstrap-variables'; 9 | // Import Bootstrap source files from node_modules 10 | @import '~bootstrap/scss/bootstrap'; 11 | 12 | /* jhipster-needle-scss-add-vendor JHipster will add new css style */ 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/login/login.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import { SharedModule } from 'app/shared/shared.module'; 5 | import { LOGIN_ROUTE } from './login.route'; 6 | import { LoginComponent } from './login.component'; 7 | 8 | @NgModule({ 9 | imports: [SharedModule, RouterModule.forChild([LOGIN_ROUTE])], 10 | declarations: [LoginComponent], 11 | }) 12 | export class LoginModule {} 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/dayjs.ts: -------------------------------------------------------------------------------- 1 | import * as dayjs from 'dayjs'; 2 | import * as customParseFormat from 'dayjs/plugin/customParseFormat'; 3 | import * as duration from 'dayjs/plugin/duration'; 4 | import * as relativeTime from 'dayjs/plugin/relativeTime'; 5 | 6 | // jhipster-needle-i18n-language-dayjs-imports - JHipster will import languages from dayjs here 7 | 8 | // DAYJS CONFIGURATION 9 | dayjs.extend(customParseFormat); 10 | dayjs.extend(duration); 11 | dayjs.extend(relativeTime); 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.model.ts: -------------------------------------------------------------------------------- 1 | export type HealthStatus = 'UP' | 'DOWN' | 'UNKNOWN' | 'OUT_OF_SERVICE'; 2 | 3 | export type HealthKey = 'diskSpace' | 'mail' | 'ping' | 'livenessState' | 'readinessState' | 'db'; 4 | 5 | export interface Health { 6 | status: HealthStatus; 7 | components: { 8 | [key in HealthKey]?: HealthDetails; 9 | }; 10 | } 11 | 12 | export interface HealthDetails { 13 | status: HealthStatus; 14 | details?: { [key: string]: unknown }; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | html 10 | text/html;charset=utf-8 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/uib-pagination.config.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { NgbPaginationConfig } from '@ng-bootstrap/ng-bootstrap'; 3 | import { ITEMS_PER_PAGE } from 'app/config/pagination.constants'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class PaginationConfig { 7 | constructor(config: NgbPaginationConfig) { 8 | config.boundaryLinks = true; 9 | config.maxSize = 5; 10 | config.pageSize = ITEMS_PER_PAGE; 11 | config.size = 'sm'; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/docker/mysql.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | gen-mysql: 4 | image: mysql:8.0.23 5 | # volumes: 6 | # - ~/volumes/jhipster/gen/mysql/:/var/lib/mysql/ 7 | environment: 8 | - MYSQL_ALLOW_EMPTY_PASSWORD=true 9 | - MYSQL_USER=sa 10 | - MYSQL_PASSWORD=yourStrong(!)Password 11 | - MYSQL_DATABASE=gen 12 | ports: 13 | - 3307:3306 14 | command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8mb4 --explicit_defaults_for_timestamp 15 | -------------------------------------------------------------------------------- /server/src/domain/base/base.entity.ts: -------------------------------------------------------------------------------- 1 | import { PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; 2 | 3 | export abstract class BaseEntity { 4 | @PrimaryGeneratedColumn() 5 | id?: number; 6 | 7 | @Column({ nullable: true }) 8 | createdBy?: string; 9 | @CreateDateColumn({ nullable: true }) 10 | createdDate?: Date; 11 | @Column({ nullable: true }) 12 | lastModifiedBy?: string; 13 | @UpdateDateColumn({ nullable: true }) 14 | lastModifiedDate?: Date; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/data-util.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { inject, TestBed } from '@angular/core/testing'; 2 | 3 | import { DataUtils } from './data-util.service'; 4 | 5 | describe('Data Utils Service Test', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [DataUtils], 9 | }); 10 | }); 11 | 12 | it('should return the bytesize of the text', inject([DataUtils], (service: DataUtils) => { 13 | expect(service.byteSize('Hello Jhipster')).toBe(`10.5 bytes`); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/configuration/configuration.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { SharedModule } from 'app/shared/shared.module'; 4 | 5 | import { ConfigurationComponent } from './configuration.component'; 6 | import { configurationRoute } from './configuration.route'; 7 | 8 | @NgModule({ 9 | imports: [SharedModule, RouterModule.forChild([configurationRoute])], 10 | declarations: [ConfigurationComponent], 11 | }) 12 | export class ConfigurationModule {} 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/app.constants.ts: -------------------------------------------------------------------------------- 1 | // These constants are injected via webpack environment variables. 2 | // You can add more variables in webpack.common.js or in profile specific webpack..js files. 3 | // If you change the values in the webpack config files, you need to re run webpack to update the application 4 | 5 | export const VERSION = process.env.VERSION; 6 | export const DEBUG_INFO_ENABLED = Boolean(process.env.DEBUG_INFO_ENABLED); 7 | export const SERVER_API_URL = process.env.SERVER_API_URL ?? ''; 8 | export const BUILD_TIMESTAMP = process.env.BUILD_TIMESTAMP; 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # We recommend you to keep these unchanged 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | # Change these settings to your own preference 16 | indent_style = space 17 | indent_size = 4 18 | 19 | [*.{ts,tsx,js,jsx,json,css,scss,yml,html,vue}] 20 | indent_size = 2 21 | 22 | [*.md] 23 | trim_trailing_whitespace = false 24 | -------------------------------------------------------------------------------- /webpack/proxy.conf.js: -------------------------------------------------------------------------------- 1 | function setupProxy() { 2 | const tls = process.env.TLS; 3 | const conf = [ 4 | { 5 | context: [ 6 | '/api', 7 | '/services', 8 | '/management', 9 | '/swagger-resources', 10 | '/v2/api-docs', 11 | '/v3/api-docs', 12 | '/h2-console', 13 | '/auth', 14 | '/health', 15 | ], 16 | target: `http${tls ? 's' : ''}://localhost:8081`, 17 | secure: false, 18 | changeOrigin: tls, 19 | }, 20 | ]; 21 | return conf; 22 | } 23 | 24 | module.exports = setupProxy(); 25 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/request/request-util.ts: -------------------------------------------------------------------------------- 1 | import { HttpParams } from '@angular/common/http'; 2 | 3 | export const createRequestOption = (req?: any): HttpParams => { 4 | let options: HttpParams = new HttpParams(); 5 | 6 | if (req) { 7 | Object.keys(req).forEach(key => { 8 | if (key !== 'sort') { 9 | options = options.set(key, req[key]); 10 | } 11 | }); 12 | 13 | if (req.sort) { 14 | req.sort.forEach((val: string) => { 15 | options = options.append('sort', val); 16 | }); 17 | } 18 | } 19 | 20 | return options; 21 | }; 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/shared-libs.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 5 | import { InfiniteScrollModule } from 'ngx-infinite-scroll'; 6 | import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; 7 | 8 | @NgModule({ 9 | exports: [FormsModule, CommonModule, NgbModule, InfiniteScrollModule, FontAwesomeModule, ReactiveFormsModule], 10 | }) 11 | export class SharedLibsModule {} 12 | -------------------------------------------------------------------------------- /server/src/service/dto/password-change.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiModelProperty } from '@nestjs/swagger'; 2 | import { IsString, IsNotEmpty } from 'class-validator'; 3 | 4 | /** 5 | * A DTO representing a password change required data - current and new password. 6 | */ 7 | export class PasswordChangeDTO { 8 | @ApiModelProperty({ description: 'Current password' }) 9 | @IsString() 10 | @IsNotEmpty() 11 | readonly currentPassword: string; 12 | 13 | @ApiModelProperty({ description: 'New password' }) 14 | @IsString() 15 | @IsNotEmpty() 16 | readonly newPassword: string; 17 | } 18 | -------------------------------------------------------------------------------- /server/src/service/dto/user-login.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiModelProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | /** 5 | * A DTO representing a login user. 6 | */ 7 | export class UserLoginDTO { 8 | @ApiModelProperty({ description: 'User password' }) 9 | @IsString() 10 | readonly password: string; 11 | 12 | @ApiModelProperty({ description: 'User remember login', required: false }) 13 | readonly rememberMe?: boolean; 14 | 15 | @ApiModelProperty({ description: 'User login name' }) 16 | @IsString() 17 | readonly username: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/config/application-config.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root', 5 | }) 6 | export class ApplicationConfigService { 7 | private endpointPrefix = ''; 8 | 9 | setEndpointPrefix(endpointPrefix: string): void { 10 | this.endpointPrefix = endpointPrefix; 11 | } 12 | 13 | getEndpointFor(api: string, microservice?: string): string { 14 | if (microservice) { 15 | return `${this.endpointPrefix}services/${microservice}/${api}`; 16 | } 17 | return `${this.endpointPrefix}${api}`; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/error/error.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'jhi-error', 6 | templateUrl: './error.component.html', 7 | }) 8 | export class ErrorComponent implements OnInit { 9 | errorMessage?: string; 10 | 11 | constructor(private route: ActivatedRoute) {} 12 | 13 | ngOnInit(): void { 14 | this.route.data.subscribe(routeData => { 15 | if (routeData.errorMessage) { 16 | this.errorMessage = routeData.errorMessage; 17 | } 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/service-worker/config/schema.json", 3 | "index": "/index.html", 4 | "assetGroups": [ 5 | { 6 | "name": "app", 7 | "installMode": "prefetch", 8 | "resources": { 9 | "files": ["/favicon.ico", "/index.html", "/manifest.webapp", "/*.css", "/*.js"] 10 | } 11 | }, 12 | { 13 | "name": "assets", 14 | "installMode": "lazy", 15 | "updateMode": "prefetch", 16 | "resources": { 17 | "files": ["/content/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { SharedModule } from 'app/shared/shared.module'; 4 | 5 | import { HealthComponent } from './health.component'; 6 | import { HealthModalComponent } from './modal/health-modal.component'; 7 | import { healthRoute } from './health.route'; 8 | 9 | @NgModule({ 10 | imports: [SharedModule, RouterModule.forChild([healthRoute])], 11 | declarations: [HealthComponent, HealthModalComponent], 12 | entryComponents: [HealthModalComponent], 13 | }) 14 | export class HealthModule {} 15 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | import { JvmMetrics } from 'app/admin/metrics/metrics.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-jvm-memory', 7 | templateUrl: './jvm-memory.component.html', 8 | }) 9 | export class JvmMemoryComponent { 10 | /** 11 | * object containing all jvm memory metrics 12 | */ 13 | @Input() jvmMemoryMetrics?: { [key: string]: JvmMetrics }; 14 | 15 | /** 16 | * boolean field saying if the metrics are in the process of being updated 17 | */ 18 | @Input() updating?: boolean; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Activation

5 | 6 |
7 | Your user account has been activated. Please 8 | sign in. 9 |
10 | 11 |
12 | Your user could not be activated. Please use the registration form to sign up. 13 |
14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /server/src/module/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { UserController } from '../web/rest/user.controller'; 3 | import { ManagementController } from '../web/rest/management.controller'; 4 | import { UserRepository } from '../repository/user.repository'; 5 | import { TypeOrmModule } from '@nestjs/typeorm'; 6 | import { UserService } from '../service/user.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([UserRepository])], 10 | controllers: [UserController, ManagementController], 11 | providers: [UserService], 12 | exports: [UserService], 13 | }) 14 | export class UserModule {} 15 | -------------------------------------------------------------------------------- /src/main/webapp/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { DEBUG_INFO_ENABLED } from './app/app.constants'; 5 | import { AppModule } from './app/app.module'; 6 | 7 | // disable debug data on prod profile to improve performance 8 | if (!DEBUG_INFO_ENABLED) { 9 | enableProdMode(); 10 | } 11 | 12 | platformBrowserDynamic() 13 | .bootstrapModule(AppModule, { preserveWhitespaces: true }) 14 | // eslint-disable-next-line no-console 15 | .then(() => console.log('Application started')) 16 | .catch(err => console.error(err)); 17 | -------------------------------------------------------------------------------- /server/src/migrations/1570200270081-CreateTables.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class CreateTables1570200270081 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | if (process.env.BACKEND_ENV === 'prod') { 6 | if (queryRunner.isTransactionActive) { 7 | await queryRunner.commitTransaction(); 8 | } 9 | 10 | await queryRunner.connection.synchronize(); 11 | } 12 | } 13 | 14 | // eslint-disable-next-line 15 | public async down(queryRunner: QueryRunner): Promise {} 16 | } 17 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/detail/user-management-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | 4 | import { User } from '../user-management.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-user-mgmt-detail', 8 | templateUrl: './user-management-detail.component.html', 9 | }) 10 | export class UserManagementDetailComponent implements OnInit { 11 | user: User | null = null; 12 | 13 | constructor(private route: ActivatedRoute) {} 14 | 15 | ngOnInit(): void { 16 | this.route.data.subscribe(({ user }) => { 17 | this.user = user; 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { Health } from './health.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class HealthService { 10 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 11 | 12 | checkHealth(): Observable { 13 | return this.http.get(this.applicationConfigService.getEndpointFor('management/health')); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/test/admin/management.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { ManagementController } from '../../src/web/rest/management.controller'; 3 | 4 | describe('Management Controller', () => { 5 | let controller: ManagementController; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | controllers: [ManagementController], 10 | }).compile(); 11 | 12 | controller = module.get(ManagementController); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(controller).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class PasswordResetInitService { 9 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 10 | 11 | save(mail: string): Observable<{}> { 12 | return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/init'), mail); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.scss: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | start Password strength bar style 3 | ========================================================================== */ 4 | ul#strength { 5 | display: inline; 6 | list-style: none; 7 | margin: 0; 8 | margin-left: 15px; 9 | padding: 0; 10 | vertical-align: 2px; 11 | } 12 | 13 | .point { 14 | background: #ddd; 15 | border-radius: 2px; 16 | display: inline-block; 17 | height: 5px; 18 | margin-right: 1px; 19 | width: 20px; 20 | &:last-child { 21 | margin: 0 !important; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | import { Services } from 'app/admin/metrics/metrics.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-metrics-endpoints-requests', 7 | templateUrl: './metrics-endpoints-requests.component.html', 8 | }) 9 | export class MetricsEndpointsRequestsComponent { 10 | /** 11 | * object containing service related metrics 12 | */ 13 | @Input() endpointsRequestsMetrics?: Services; 14 | 15 | /** 16 | * boolean field saying if the metrics are in the process of being updated 17 | */ 18 | @Input() updating?: boolean; 19 | } 20 | -------------------------------------------------------------------------------- /server/src/client/interceptors/logging.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { CallHandler, ExecutionContext, Injectable, NestInterceptor, Logger } from '@nestjs/common'; 2 | import { Observable } from 'rxjs'; 3 | import { Request } from 'express'; 4 | 5 | @Injectable() 6 | export class LoggingInterceptor implements NestInterceptor { 7 | intercept(context: ExecutionContext, next: CallHandler): Observable { 8 | const req: Request = context.switchToHttp().getRequest(); 9 | Logger.debug( 10 | `${context.getClass().name}.${context.getHandler().name}() : ${req.method} ${req.url}`, 11 | 'LoggingInterceptor', 12 | ); 13 | return next.handle(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpParams } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class ActivateService { 9 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 10 | 11 | get(key: string): Observable<{}> { 12 | return this.http.get(this.applicationConfigService.getEndpointFor('api/activate'), { 13 | params: new HttpParams().set('key', key), 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class PasswordService { 9 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 10 | 11 | save(newPassword: string, currentPassword: string): Observable<{}> { 12 | return this.http.post(this.applicationConfigService.getEndpointFor('api/account/change-password'), { currentPassword, newPassword }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/register/register.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { Registration } from './register.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class RegisterService { 10 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 11 | 12 | save(registration: Registration): Observable<{}> { 13 | return this.http.post(this.applicationConfigService.getEndpointFor('api/register'), registration); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/auth/state-storage.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { SessionStorageService } from 'ngx-webstorage'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class StateStorageService { 6 | private previousUrlKey = 'previousUrl'; 7 | 8 | constructor(private $sessionStorage: SessionStorageService) {} 9 | 10 | storeUrl(url: string): void { 11 | this.$sessionStorage.store(this.previousUrlKey, url); 12 | } 13 | 14 | getUrl(): string | null { 15 | return this.$sessionStorage.retrieve(this.previousUrlKey) as string | null; 16 | } 17 | 18 | clearUrl(): void { 19 | this.$sessionStorage.clear(this.previousUrlKey); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | import { GarbageCollector } from 'app/admin/metrics/metrics.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-metrics-garbagecollector', 7 | templateUrl: './metrics-garbagecollector.component.html', 8 | }) 9 | export class MetricsGarbageCollectorComponent { 10 | /** 11 | * object containing garbage collector related metrics 12 | */ 13 | @Input() garbageCollectorMetrics?: GarbageCollector; 14 | 15 | /** 16 | * boolean field saying if the metrics are in the process of being updated 17 | */ 18 | @Input() updating?: boolean; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class PasswordResetFinishService { 9 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 10 | 11 | save(key: string, newPassword: string): Observable<{}> { 12 | return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/finish'), { key, newPassword }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/datepicker-adapter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular bootstrap Date adapter 3 | */ 4 | import { Injectable } from '@angular/core'; 5 | import { NgbDateAdapter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; 6 | import * as dayjs from 'dayjs'; 7 | 8 | @Injectable() 9 | export class NgbDateDayjsAdapter extends NgbDateAdapter { 10 | fromModel(date: dayjs.Dayjs | null): NgbDateStruct | null { 11 | if (date && dayjs.isDayjs(date) && date.isValid()) { 12 | return { year: date.year(), month: date.month() + 1, day: date.date() }; 13 | } 14 | return null; 15 | } 16 | 17 | toModel(date: NgbDateStruct | null): dayjs.Dayjs | null { 18 | return date ? dayjs(`${date.year}-${date.month}-${date.day}`) : null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-date.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import * as dayjs from 'dayjs'; 2 | 3 | import { FormatMediumDatePipe } from './format-medium-date.pipe'; 4 | 5 | describe('FormatMediumDatePipe', () => { 6 | const formatMediumDatePipe = new FormatMediumDatePipe(); 7 | 8 | it('should return an empty string when receive undefined', () => { 9 | expect(formatMediumDatePipe.transform(undefined)).toBe(''); 10 | }); 11 | 12 | it('should return an empty string when receive null', () => { 13 | expect(formatMediumDatePipe.transform(null)).toBe(''); 14 | }); 15 | 16 | it('should format date like this D MMM YYYY', () => { 17 | expect(formatMediumDatePipe.transform(dayjs('2020-11-16').locale('fr'))).toBe('16 Nov 2020'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/account.route.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { activateRoute } from './activate/activate.route'; 4 | import { passwordRoute } from './password/password.route'; 5 | import { passwordResetFinishRoute } from './password-reset/finish/password-reset-finish.route'; 6 | import { passwordResetInitRoute } from './password-reset/init/password-reset-init.route'; 7 | import { registerRoute } from './register/register.route'; 8 | import { settingsRoute } from './settings/settings.route'; 9 | 10 | const ACCOUNT_ROUTES = [activateRoute, passwordRoute, passwordResetFinishRoute, passwordResetInitRoute, registerRoute, settingsRoute]; 11 | 12 | export const accountState: Routes = [ 13 | { 14 | path: '', 15 | children: ACCOUNT_ROUTES, 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-datetime.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import * as dayjs from 'dayjs'; 2 | 3 | import { FormatMediumDatetimePipe } from './format-medium-datetime.pipe'; 4 | 5 | describe('FormatMediumDatePipe', () => { 6 | const formatMediumDatetimePipe = new FormatMediumDatetimePipe(); 7 | 8 | it('should return an empty string when receive undefined', () => { 9 | expect(formatMediumDatetimePipe.transform(undefined)).toBe(''); 10 | }); 11 | 12 | it('should return an empty string when receive null', () => { 13 | expect(formatMediumDatetimePipe.transform(null)).toBe(''); 14 | }); 15 | 16 | it('should format date like this D MMM YYYY', () => { 17 | expect(formatMediumDatetimePipe.transform(dayjs('2020-11-16').locale('fr'))).toBe('16 Nov 2020 00:00:00'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { mergeMap } from 'rxjs/operators'; 4 | 5 | import { ActivateService } from './activate.service'; 6 | 7 | @Component({ 8 | selector: 'jhi-activate', 9 | templateUrl: './activate.component.html', 10 | }) 11 | export class ActivateComponent implements OnInit { 12 | error = false; 13 | success = false; 14 | 15 | constructor(private activateService: ActivateService, private route: ActivatedRoute) {} 16 | 17 | ngOnInit(): void { 18 | this.route.queryParams.pipe(mergeMap(params => this.activateService.get(params.key))).subscribe( 19 | () => (this.success = true), 20 | () => (this.error = true) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/scripts/copy-resources.ts: -------------------------------------------------------------------------------- 1 | import * as shell from 'shelljs'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | const out = path.join(__dirname, '..', 'dist'); 6 | createFolderIfNotExist(out); 7 | 8 | createFolderIfNotExist(path.join(out, 'config')); 9 | 10 | shell.cp('-R', 'src/config/*.yml', 'dist/config'); 11 | 12 | let clientDist = path.join(__dirname, '..', '..', 'target', 'classes', 'static'); 13 | 14 | if (!fs.existsSync(clientDist)) { 15 | clientDist = path.join(__dirname, '..', '..', 'build', 'resources', 'main', 'static'); 16 | } 17 | 18 | if (fs.existsSync(clientDist)) { 19 | shell.cp('-R', clientDist, out); 20 | } 21 | 22 | function createFolderIfNotExist(outDir: string): void { 23 | if (!fs.existsSync(outDir)) { 24 | fs.mkdirSync(outDir); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/error/error.route.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { ErrorComponent } from './error.component'; 4 | 5 | export const errorRoute: Routes = [ 6 | { 7 | path: 'error', 8 | component: ErrorComponent, 9 | data: { 10 | pageTitle: 'Error page!', 11 | }, 12 | }, 13 | { 14 | path: 'accessdenied', 15 | component: ErrorComponent, 16 | data: { 17 | pageTitle: 'Error page!', 18 | errorMessage: 'You are not authorized to access this page.', 19 | }, 20 | }, 21 | { 22 | path: '404', 23 | component: ErrorComponent, 24 | data: { 25 | pageTitle: 'Error page!', 26 | errorMessage: 'The page does not exist.', 27 | }, 28 | }, 29 | { 30 | path: '**', 31 | redirectTo: '/404', 32 | }, 33 | ]; 34 | -------------------------------------------------------------------------------- /server/src/security/guards/roles.guard.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; 2 | import { Reflector } from '@nestjs/core'; 3 | import { UserDTO } from '../../service/dto/user.dto'; 4 | 5 | @Injectable() 6 | export class RolesGuard implements CanActivate { 7 | constructor(private readonly reflector: Reflector) {} 8 | 9 | canActivate(context: ExecutionContext): boolean { 10 | const roles = this.reflector.get('roles', context.getHandler()); 11 | 12 | if (!roles) { 13 | return true; 14 | } 15 | 16 | const request = context.switchToHttp().getRequest(); 17 | const user = request.user as UserDTO; 18 | 19 | return user && user.authorities && user.authorities.some(role => roles.includes(role)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/configuration/configuration.model.ts: -------------------------------------------------------------------------------- 1 | export interface ConfigProps { 2 | contexts: Contexts; 3 | } 4 | 5 | export interface Contexts { 6 | [key: string]: Context; 7 | } 8 | 9 | export interface Context { 10 | beans: Beans; 11 | parentId?: any; 12 | } 13 | 14 | export interface Beans { 15 | [key: string]: Bean; 16 | } 17 | 18 | export interface Bean { 19 | prefix: string; 20 | properties: any; 21 | } 22 | 23 | export interface Env { 24 | activeProfiles?: string[]; 25 | propertySources: PropertySource[]; 26 | } 27 | 28 | export interface PropertySource { 29 | name: string; 30 | properties: Properties; 31 | } 32 | 33 | export interface Properties { 34 | [key: string]: Property; 35 | } 36 | 37 | export interface Property { 38 | value: string; 39 | origin?: string; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; 2 | 3 | import { CacheMetrics } from 'app/admin/metrics/metrics.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-metrics-cache', 7 | templateUrl: './metrics-cache.component.html', 8 | changeDetection: ChangeDetectionStrategy.OnPush, 9 | }) 10 | export class MetricsCacheComponent { 11 | /** 12 | * object containing all cache related metrics 13 | */ 14 | @Input() cacheMetrics?: { [key: string]: CacheMetrics }; 15 | 16 | /** 17 | * boolean field saying if the metrics are in the process of being updated 18 | */ 19 | @Input() updating?: boolean; 20 | 21 | filterNaN(input: number): number { 22 | return isNaN(input) ? 0 : input; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; 2 | 3 | import { Databases } from 'app/admin/metrics/metrics.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-metrics-datasource', 7 | templateUrl: './metrics-datasource.component.html', 8 | changeDetection: ChangeDetectionStrategy.OnPush, 9 | }) 10 | export class MetricsDatasourceComponent { 11 | /** 12 | * object containing all datasource related metrics 13 | */ 14 | @Input() datasourceMetrics?: Databases; 15 | 16 | /** 17 | * boolean field saying if the metrics are in the process of being updated 18 | */ 19 | @Input() updating?: boolean; 20 | 21 | filterNaN(input: number): number { 22 | return isNaN(input) ? 0 : input; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; 2 | 3 | import { HttpServerRequests } from 'app/admin/metrics/metrics.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-metrics-request', 7 | templateUrl: './metrics-request.component.html', 8 | changeDetection: ChangeDetectionStrategy.OnPush, 9 | }) 10 | export class MetricsRequestComponent { 11 | /** 12 | * object containing http request related metrics 13 | */ 14 | @Input() requestMetrics?: HttpServerRequests; 15 | 16 | /** 17 | * boolean field saying if the metrics are in the process of being updated 18 | */ 19 | @Input() updating?: boolean; 20 | 21 | filterNaN(input: number): number { 22 | return isNaN(input) ? 0 : input; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/page-ribbon.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { map } from 'rxjs/operators'; 4 | import { ProfileService } from './profile.service'; 5 | 6 | @Component({ 7 | selector: 'jhi-page-ribbon', 8 | template: ` 9 |
10 | {{ ribbonEnv }} 11 |
12 | `, 13 | styleUrls: ['./page-ribbon.component.scss'], 14 | }) 15 | export class PageRibbonComponent implements OnInit { 16 | ribbonEnv$?: Observable; 17 | 18 | constructor(private profileService: ProfileService) {} 19 | 20 | ngOnInit(): void { 21 | this.ribbonEnv$ = this.profileService.getProfileInfo().pipe(map(profileInfo => profileInfo.ribbonEnv)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/metrics.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { Metrics, ThreadDump } from './metrics.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class MetricsService { 10 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 11 | 12 | getMetrics(): Observable { 13 | return this.http.get(this.applicationConfigService.getEndpointFor('management/jhimetrics')); 14 | } 15 | 16 | threadDump(): Observable { 17 | return this.http.get(this.applicationConfigService.getEndpointFor('management/threaddump')); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/swagger.ts: -------------------------------------------------------------------------------- 1 | import { Logger, INestApplication } from '@nestjs/common'; 2 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 3 | import { config } from './config'; 4 | 5 | export function setupSwagger(app: INestApplication): any { 6 | const logger: Logger = new Logger('Swagger'); 7 | const swaggerEndpoint = config.get('jhipster.swagger.path'); 8 | 9 | const options = new DocumentBuilder() 10 | .setTitle(config.get('jhipster.swagger.title')) 11 | .setDescription(config.get('jhipster.swagger.description')) 12 | .setVersion(config.get('jhipster.swagger.version')) 13 | .addBearerAuth() 14 | .build(); 15 | const document = SwaggerModule.createDocument(app, options); 16 | SwaggerModule.setup(swaggerEndpoint, app, document); 17 | logger.log(`Added swagger on endpoint ${swaggerEndpoint}`); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/webapp/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Gen", 3 | "short_name": "Gen", 4 | "icons": [ 5 | { 6 | "src": "./content/images/jhipster_family_member_0_head-192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./content/images/jhipster_family_member_0_head-256.png", 12 | "sizes": "256x256", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "./content/images/jhipster_family_member_0_head-384.png", 17 | "sizes": "384x384", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "./content/images/jhipster_family_member_0_head-512.png", 22 | "sizes": "512x512", 23 | "type": "image/png" 24 | } 25 | ], 26 | "theme_color": "#000000", 27 | "background_color": "#e0e0e0", 28 | "start_url": ".", 29 | "display": "standalone", 30 | "orientation": "portrait" 31 | } 32 | -------------------------------------------------------------------------------- /server/src/web/rest/management.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Logger, UseInterceptors } from '@nestjs/common'; 2 | import { LoggingInterceptor } from '../../client/interceptors/logging.interceptor'; 3 | import { ApiUseTags, ApiResponse, ApiOperation, ApiExcludeEndpoint } from '@nestjs/swagger'; 4 | 5 | @Controller('management') 6 | @UseInterceptors(LoggingInterceptor) 7 | @ApiUseTags('management-controller') 8 | export class ManagementController { 9 | logger = new Logger('ManagementController'); 10 | 11 | @ApiExcludeEndpoint() 12 | @Get('/info') 13 | @ApiOperation({ title: 'Microservice Info' }) 14 | @ApiResponse({ 15 | status: 200, 16 | description: 'Check if the microservice is up', 17 | }) 18 | info(): any { 19 | return { 20 | activeProfiles: 'dev', 21 | 'display-ribbon-on-profiles': 'dev', 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/logs.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { LoggersResponse, Level } from './log.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class LogsService { 10 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 11 | 12 | changeLevel(name: string, configuredLevel: Level): Observable<{}> { 13 | return this.http.post(this.applicationConfigService.getEndpointFor('management/loggers/' + name), { configuredLevel }); 14 | } 15 | 16 | findAll(): Observable { 17 | return this.http.get(this.applicationConfigService.getEndpointFor('management/loggers')); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 3 | 4 | import { User } from '../user-management.model'; 5 | import { UserManagementService } from '../service/user-management.service'; 6 | 7 | @Component({ 8 | selector: 'jhi-user-mgmt-delete-dialog', 9 | templateUrl: './user-management-delete-dialog.component.html', 10 | }) 11 | export class UserManagementDeleteDialogComponent { 12 | user?: User; 13 | 14 | constructor(private userService: UserManagementService, public activeModal: NgbActiveModal) {} 15 | 16 | cancel(): void { 17 | this.activeModal.dismiss(); 18 | } 19 | 20 | confirmDelete(login: string): void { 21 | this.userService.delete(login).subscribe(() => { 22 | this.activeModal.close('deleted'); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 8 | 13 | 14 | 21 |
22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "es2020", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "experimentalDecorators": true, 8 | "strict": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "skipLibCheck": true, 12 | "outDir": "target/classes/static/app", 13 | "lib": ["es2018", "dom"], 14 | "baseUrl": "./", 15 | "paths": { 16 | "app/*": ["src/main/webapp/app/*"] 17 | }, 18 | "importHelpers": true, 19 | "downlevelIteration": true, 20 | "declaration": false, 21 | "forceConsistentCasingInFileNames": true 22 | }, 23 | "angularCompilerOptions": { 24 | "strictInjectionParameters": true, 25 | "strictTemplates": true, 26 | "strictInputAccessModifiers": true, 27 | "preserveWhitespaces": true 28 | }, 29 | "exclude": ["node_modules", "server"] 30 | } 31 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line. 18 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 19 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/user-management.model.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | id?: number; 3 | login?: string; 4 | firstName?: string | null; 5 | lastName?: string | null; 6 | email?: string; 7 | activated?: boolean; 8 | langKey?: string; 9 | authorities?: string[]; 10 | createdBy?: string; 11 | createdDate?: Date; 12 | lastModifiedBy?: string; 13 | lastModifiedDate?: Date; 14 | } 15 | 16 | export class User implements IUser { 17 | constructor( 18 | public id?: number, 19 | public login?: string, 20 | public firstName?: string | null, 21 | public lastName?: string | null, 22 | public email?: string, 23 | public activated?: boolean, 24 | public langKey?: string, 25 | public authorities?: string[], 26 | public createdBy?: string, 27 | public createdDate?: Date, 28 | public lastModifiedBy?: string, 29 | public lastModifiedDate?: Date 30 | ) {} 31 | } 32 | -------------------------------------------------------------------------------- /src/main/webapp/app/login/login.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { mergeMap } from 'rxjs/operators'; 4 | 5 | import { Account } from 'app/core/auth/account.model'; 6 | import { AccountService } from 'app/core/auth/account.service'; 7 | import { AuthServerProvider } from 'app/core/auth/auth-jwt.service'; 8 | import { Login } from './login.model'; 9 | 10 | @Injectable({ providedIn: 'root' }) 11 | export class LoginService { 12 | constructor(private accountService: AccountService, private authServerProvider: AuthServerProvider) {} 13 | 14 | login(credentials: Login): Observable { 15 | return this.authServerProvider.login(credentials).pipe(mergeMap(() => this.accountService.identity(true))); 16 | } 17 | 18 | logout(): void { 19 | this.authServerProvider.logout().subscribe({ complete: () => this.accountService.authenticate(null) }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from '@angular/core'; 2 | 3 | import { AlertService, Alert } from 'app/core/util/alert.service'; 4 | 5 | @Component({ 6 | selector: 'jhi-alert', 7 | templateUrl: './alert.component.html', 8 | }) 9 | export class AlertComponent implements OnInit, OnDestroy { 10 | alerts: Alert[] = []; 11 | 12 | constructor(private alertService: AlertService) {} 13 | 14 | ngOnInit(): void { 15 | this.alerts = this.alertService.get(); 16 | } 17 | 18 | setClasses(alert: Alert): { [key: string]: boolean } { 19 | const classes = { 'jhi-toast': Boolean(alert.toast) }; 20 | if (alert.position) { 21 | return { ...classes, [alert.position]: true }; 22 | } 23 | return classes; 24 | } 25 | 26 | ngOnDestroy(): void { 27 | this.alertService.clear(); 28 | } 29 | 30 | close(alert: Alert): void { 31 | alert.close?.(this.alerts); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/src/service/mapper/user.mapper.ts: -------------------------------------------------------------------------------- 1 | import { User } from '../../domain/user.entity'; 2 | import { UserDTO } from '../dto/user.dto'; 3 | 4 | /** 5 | * An User mapper object. 6 | */ 7 | export class UserMapper { 8 | static fromDTOtoEntity(userDTO: UserDTO): User { 9 | if (!userDTO) { 10 | return; 11 | } 12 | const user = new User(); 13 | const fields = Object.getOwnPropertyNames(userDTO); 14 | fields.forEach(field => { 15 | user[field] = userDTO[field]; 16 | }); 17 | return user; 18 | } 19 | 20 | static fromEntityToDTO(user: User): UserDTO { 21 | if (!user) { 22 | return; 23 | } 24 | const userDTO = new UserDTO(); 25 | 26 | const fields = Object.getOwnPropertyNames(user); 27 | 28 | fields.forEach(field => { 29 | userDTO[field] = user[field]; 30 | }); 31 | 32 | return userDTO; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/page-ribbon.component.scss: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Developement Ribbon 3 | ========================================================================== */ 4 | .ribbon { 5 | background-color: rgba(170, 0, 0, 0.5); 6 | left: -3.5em; 7 | -moz-transform: rotate(-45deg); 8 | -ms-transform: rotate(-45deg); 9 | -o-transform: rotate(-45deg); 10 | -webkit-transform: rotate(-45deg); 11 | transform: rotate(-45deg); 12 | overflow: hidden; 13 | position: absolute; 14 | top: 40px; 15 | white-space: nowrap; 16 | width: 15em; 17 | z-index: 9999; 18 | pointer-events: none; 19 | opacity: 0.75; 20 | a { 21 | color: #fff; 22 | display: block; 23 | font-weight: 400; 24 | margin: 1px 0; 25 | padding: 10px 50px; 26 | text-align: center; 27 | text-decoration: none; 28 | text-shadow: 0 0 5px #444; 29 | pointer-events: none; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/index.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | 3 | import { AuthInterceptor } from 'app/core/interceptor/auth.interceptor'; 4 | import { AuthExpiredInterceptor } from 'app/core/interceptor/auth-expired.interceptor'; 5 | import { ErrorHandlerInterceptor } from 'app/core/interceptor/error-handler.interceptor'; 6 | import { NotificationInterceptor } from 'app/core/interceptor/notification.interceptor'; 7 | 8 | export const httpInterceptorProviders = [ 9 | { 10 | provide: HTTP_INTERCEPTORS, 11 | useClass: AuthInterceptor, 12 | multi: true, 13 | }, 14 | { 15 | provide: HTTP_INTERCEPTORS, 16 | useClass: AuthExpiredInterceptor, 17 | multi: true, 18 | }, 19 | { 20 | provide: HTTP_INTERCEPTORS, 21 | useClass: ErrorHandlerInterceptor, 22 | multi: true, 23 | }, 24 | { 25 | provide: HTTP_INTERCEPTORS, 26 | useClass: NotificationInterceptor, 27 | multi: true, 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/home/home.component.scss: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Main page styles 3 | ========================================================================== */ 4 | 5 | .hipster { 6 | display: inline-block; 7 | width: 347px; 8 | height: 497px; 9 | background: url('../../content/images/jhipster_family_member_0.svg') no-repeat center top; 10 | background-size: contain; 11 | } 12 | 13 | /* wait autoprefixer update to allow simple generation of high pixel density media query */ 14 | @media only screen and (-webkit-min-device-pixel-ratio: 2), 15 | only screen and (-moz-min-device-pixel-ratio: 2), 16 | only screen and (-o-min-device-pixel-ratio: 2/1), 17 | only screen and (min-resolution: 192dpi), 18 | only screen and (min-resolution: 2dppx) { 19 | .hipster { 20 | background: url('../../content/images/jhipster_family_member_0.svg') no-repeat center top; 21 | background-size: contain; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/error-handler.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpInterceptor, HttpRequest, HttpErrorResponse, HttpHandler, HttpEvent } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { tap } from 'rxjs/operators'; 5 | 6 | import { EventManager, EventWithContent } from 'app/core/util/event-manager.service'; 7 | 8 | @Injectable() 9 | export class ErrorHandlerInterceptor implements HttpInterceptor { 10 | constructor(private eventManager: EventManager) {} 11 | 12 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 13 | return next.handle(request).pipe( 14 | tap({ 15 | error: (err: HttpErrorResponse) => { 16 | if (!(err.status === 401 && (err.message === '' || err.url?.includes('api/account')))) { 17 | this.eventManager.broadcast(new EventWithContent('genApp.httpError', err)); 18 | } 19 | }, 20 | }) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/user-management.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import { SharedModule } from 'app/shared/shared.module'; 5 | import { UserManagementComponent } from './list/user-management.component'; 6 | import { UserManagementDetailComponent } from './detail/user-management-detail.component'; 7 | import { UserManagementUpdateComponent } from './update/user-management-update.component'; 8 | import { UserManagementDeleteDialogComponent } from './delete/user-management-delete-dialog.component'; 9 | import { userManagementRoute } from './user-management.route'; 10 | 11 | @NgModule({ 12 | imports: [SharedModule, RouterModule.forChild(userManagementRoute)], 13 | declarations: [ 14 | UserManagementComponent, 15 | UserManagementDetailComponent, 16 | UserManagementUpdateComponent, 17 | UserManagementDeleteDialogComponent, 18 | ], 19 | entryComponents: [UserManagementDeleteDialogComponent], 20 | }) 21 | export class UserManagementModule {} 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.html: -------------------------------------------------------------------------------- 1 |

Memory

2 | 3 |
4 |
5 | 6 | {{ entry.key }} 7 | ({{ entry.value.used / 1048576 | number: '1.0-0' }}M / {{ entry.value.max / 1048576 | number: '1.0-0' }}M) 8 | 9 | 10 |
Committed : {{ entry.value.committed / 1048576 | number: '1.0-0' }}M
11 | 12 | {{ entry.key }} {{ entry.value.used / 1048576 | number: '1.0-0' }}M 17 | 18 | 25 | {{ (entry.value.used * 100) / entry.value.max | number: '1.0-0' }}% 26 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/logs.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; 3 | 4 | import { LogsService } from './logs.service'; 5 | 6 | describe('Service Tests', () => { 7 | describe('Logs Service', () => { 8 | let service: LogsService; 9 | let httpMock: HttpTestingController; 10 | 11 | beforeEach(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [HttpClientTestingModule], 14 | }); 15 | 16 | service = TestBed.inject(LogsService); 17 | httpMock = TestBed.inject(HttpTestingController); 18 | }); 19 | 20 | afterEach(() => { 21 | httpMock.verify(); 22 | }); 23 | 24 | describe('Service methods', () => { 25 | it('should change log level', () => { 26 | service.changeLevel('main', 'ERROR').subscribe(); 27 | 28 | const req = httpMock.expectOne({ method: 'POST' }); 29 | expect(req.request.body).toEqual({ configuredLevel: 'ERROR' }); 30 | }); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.html: -------------------------------------------------------------------------------- 1 |

Endpoints requests (time in millisecond)

2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
MethodEndpoint urlCountMean
{{ method.key }}{{ entry.key }}{{ method.value!.count }}{{ method.value!.mean | number: '1.0-3' }}
24 |
25 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.html: -------------------------------------------------------------------------------- 1 |

HTTP requests (time in millisecond)

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 23 | 24 | 25 | 26 |
CodeCountMeanMax
{{ entry.key }} 16 | 17 | {{ entry.value.count }} 18 | 19 | 21 | {{ filterNaN(entry.value.mean) | number: '1.0-2' }} 22 | {{ entry.value.max | number: '1.0-2' }}
27 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/modal/health-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 3 | 4 | import { HealthKey, HealthDetails } from '../health.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-health-modal', 8 | templateUrl: './health-modal.component.html', 9 | }) 10 | export class HealthModalComponent { 11 | health?: { key: HealthKey; value: HealthDetails }; 12 | 13 | constructor(public activeModal: NgbActiveModal) {} 14 | 15 | readableValue(value: any): string { 16 | if (this.health?.key === 'diskSpace') { 17 | // Should display storage space in an human readable unit 18 | const val = value / 1073741824; 19 | if (val > 1) { 20 | return val.toFixed(2) + ' GB'; 21 | } else { 22 | return (value / 1048576).toFixed(2) + ' MB'; 23 | } 24 | } 25 | 26 | if (typeof value === 'object') { 27 | return JSON.stringify(value); 28 | } else { 29 | return String(value); 30 | } 31 | } 32 | 33 | dismiss(): void { 34 | this.activeModal.dismiss(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/navbar/navbar.component.scss: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/scss/functions'; 2 | @import '~bootstrap/scss/variables'; 3 | 4 | /* ========================================================================== 5 | Navbar 6 | ========================================================================== */ 7 | 8 | .navbar-version { 9 | font-size: 0.65em; 10 | color: $navbar-dark-color; 11 | } 12 | 13 | .profile-image { 14 | height: 1.75em; 15 | width: 1.75em; 16 | } 17 | 18 | .navbar { 19 | padding: 0.2rem 1rem; 20 | 21 | ul.navbar-nav { 22 | .nav-item { 23 | margin-left: 0.5em; 24 | } 25 | } 26 | 27 | a.nav-link { 28 | font-weight: 400; 29 | } 30 | } 31 | 32 | /* ========================================================================== 33 | Logo styles 34 | ========================================================================== */ 35 | .logo-img { 36 | height: 45px; 37 | width: 45px; 38 | display: inline-block; 39 | vertical-align: middle; 40 | background: url('../../../content/images/logo-jhipster.png') no-repeat center center; 41 | background-size: contain; 42 | } 43 | -------------------------------------------------------------------------------- /server/webpack.server.prod.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | target: 'node', 6 | entry: { 7 | app: ['./src/main.ts'], 8 | }, 9 | resolve: { 10 | extensions: ['.ts', '.js'], 11 | alias: {}, 12 | }, 13 | mode: 'production', 14 | node: { 15 | __filename: false, 16 | __dirname: false, 17 | }, 18 | output: { 19 | path: path.resolve(__dirname, 'dist'), 20 | filename: 'bundle.js', 21 | }, 22 | module: { 23 | rules: [ 24 | { 25 | use: 'ts-loader', 26 | test: /\.ts?$/, 27 | exclude: /node_module/, 28 | }, 29 | ], 30 | }, 31 | plugins: [ 32 | new webpack.EnvironmentPlugin({ 33 | BACKEND_ENV: 'prod', 34 | }), 35 | ], 36 | externals: { 37 | '@nestjs/microservices': 'nestjs/microservices', 38 | 'cache-manager': 'cache-manager', 39 | 'fastify-swagger': 'fastify-swagger', 40 | 'aws-sdk': 'aws-sdk', 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /server/src/domain/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { Authority } from './authority.entity'; 2 | import { Entity, Column, ManyToMany, JoinTable } from 'typeorm'; 3 | import { BaseEntity } from './base/base.entity'; 4 | import { Exclude } from 'class-transformer'; 5 | 6 | @Entity('nhi_user') 7 | export class User extends BaseEntity { 8 | @Column({ unique: true }) 9 | login: string; 10 | @Column({ nullable: true }) 11 | firstName?: string; 12 | @Column({ nullable: true }) 13 | lastName?: string; 14 | @Column() 15 | email: string; 16 | @Column({ default: false }) 17 | activated?: boolean; 18 | @Column({ default: 'en' }) 19 | langKey?: string; 20 | 21 | @ManyToMany(() => Authority) 22 | @JoinTable() 23 | authorities?: any[]; 24 | 25 | @Column({ 26 | type: 'varchar', 27 | }) 28 | @Exclude() 29 | password: string; 30 | @Column({ nullable: true }) 31 | imageUrl?: string; 32 | @Column({ nullable: true }) 33 | activationKey?: string; 34 | @Column({ nullable: true }) 35 | resetKey?: string; 36 | @Column({ nullable: true }) 37 | resetDate?: Date; 38 | } 39 | -------------------------------------------------------------------------------- /server/src/security/passport.jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { ExtractJwt, Strategy, VerifiedCallback } from 'passport-jwt'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 4 | import { Payload } from './payload.interface'; 5 | import { config } from '../config'; 6 | import { AuthService } from '../service/auth.service'; 7 | 8 | @Injectable() 9 | export class JwtStrategy extends PassportStrategy(Strategy) { 10 | constructor(private readonly authService: AuthService) { 11 | super({ 12 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 13 | ignoreExpiration: true, 14 | secretOrKey: config['jhipster.security.authentication.jwt.base64-secret'], 15 | }); 16 | } 17 | 18 | async validate(payload: Payload, done: VerifiedCallback): Promise { 19 | const user = await this.authService.validateUser(payload); 20 | if (!user) { 21 | return done(new UnauthorizedException({ message: 'user does not exist' }), false); 22 | } 23 | 24 | return done(null, user); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; 2 | import { FormBuilder, Validators } from '@angular/forms'; 3 | 4 | import { PasswordResetInitService } from './password-reset-init.service'; 5 | 6 | @Component({ 7 | selector: 'jhi-password-reset-init', 8 | templateUrl: './password-reset-init.component.html', 9 | }) 10 | export class PasswordResetInitComponent implements AfterViewInit { 11 | @ViewChild('email', { static: false }) 12 | email?: ElementRef; 13 | 14 | success = false; 15 | resetRequestForm = this.fb.group({ 16 | email: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email]], 17 | }); 18 | 19 | constructor(private passwordResetInitService: PasswordResetInitService, private fb: FormBuilder) {} 20 | 21 | ngAfterViewInit(): void { 22 | if (this.email) { 23 | this.email.nativeElement.focus(); 24 | } 25 | } 26 | 27 | requestReset(): void { 28 | this.passwordResetInitService.save(this.resetRequestForm.get(['email'])!.value).subscribe(() => (this.success = true)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/webapp/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Subscription } from 'rxjs'; 4 | 5 | import { AccountService } from 'app/core/auth/account.service'; 6 | import { Account } from 'app/core/auth/account.model'; 7 | 8 | @Component({ 9 | selector: 'jhi-home', 10 | templateUrl: './home.component.html', 11 | styleUrls: ['./home.component.scss'], 12 | }) 13 | export class HomeComponent implements OnInit, OnDestroy { 14 | account: Account | null = null; 15 | authSubscription?: Subscription; 16 | 17 | constructor(private accountService: AccountService, private router: Router) {} 18 | 19 | ngOnInit(): void { 20 | this.authSubscription = this.accountService.getAuthenticationState().subscribe(account => (this.account = account)); 21 | } 22 | 23 | isAuthenticated(): boolean { 24 | return this.accountService.isAuthenticated(); 25 | } 26 | 27 | login(): void { 28 | this.router.navigate(['/login']); 29 | } 30 | 31 | ngOnDestroy(): void { 32 | if (this.authSubscription) { 33 | this.authSubscription.unsubscribe(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/src/domain/base/pagination.entity.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-classes-per-file */ 2 | import { BaseEntity } from './base.entity'; 3 | import { Type, Expose as JsonProperty } from 'class-transformer'; 4 | 5 | export class Sort { 6 | public property: string; 7 | public direction: 'ASC' | 'DESC' | string; 8 | constructor(sort: string) { 9 | if (sort) { 10 | [this.property, this.direction] = sort.split(','); 11 | } 12 | } 13 | 14 | asOrder(): any { 15 | const order = {}; 16 | order[this.property] = this.direction; 17 | return order; 18 | } 19 | } 20 | 21 | export class PageRequest { 22 | @JsonProperty() 23 | page = 0; 24 | @JsonProperty() 25 | size = 20; 26 | @Type(() => Sort) 27 | sort: Sort = new Sort('id,ASC'); 28 | 29 | constructor(page: number | string, size: number | string, sort: string) { 30 | this.page = +page || this.page; 31 | this.size = +size || this.size; 32 | this.sort = sort ? new Sort(sort) : this.sort; 33 | } 34 | } 35 | 36 | export class Page { 37 | constructor(public content: T[], public total: number, public pageable: PageRequest) {} 38 | } 39 | -------------------------------------------------------------------------------- /server/src/web/rest/user.jwt.controller.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Logger, Post, Res, Req, UseInterceptors } from '@nestjs/common'; 2 | import { Response, Request } from 'express'; 3 | import { UserLoginDTO } from '../../service/dto/user-login.dto'; 4 | import { AuthService } from '../../service/auth.service'; 5 | import { LoggingInterceptor } from '../../client/interceptors/logging.interceptor'; 6 | import { ApiUseTags, ApiResponse, ApiOperation } from '@nestjs/swagger'; 7 | 8 | @Controller('api') 9 | @UseInterceptors(LoggingInterceptor) 10 | @ApiUseTags('user-jwt-controller') 11 | export class UserJWTController { 12 | logger = new Logger('UserJWTController'); 13 | 14 | constructor(private readonly authService: AuthService) {} 15 | 16 | @Post('/authenticate') 17 | @ApiOperation({ title: 'Authorization api retrieving token' }) 18 | @ApiResponse({ 19 | status: 201, 20 | description: 'Authorized', 21 | }) 22 | async authorize(@Req() req: Request, @Body() user: UserLoginDTO, @Res() res: Response): Promise { 23 | const jwt = await this.authService.login(user); 24 | res.setHeader('Authorization', 'Bearer ' + jwt.id_token); 25 | return res.json(jwt); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/account.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import { SharedModule } from 'app/shared/shared.module'; 5 | import { PasswordStrengthBarComponent } from './password/password-strength-bar/password-strength-bar.component'; 6 | import { RegisterComponent } from './register/register.component'; 7 | import { ActivateComponent } from './activate/activate.component'; 8 | import { PasswordComponent } from './password/password.component'; 9 | import { PasswordResetInitComponent } from './password-reset/init/password-reset-init.component'; 10 | import { PasswordResetFinishComponent } from './password-reset/finish/password-reset-finish.component'; 11 | import { SettingsComponent } from './settings/settings.component'; 12 | import { accountState } from './account.route'; 13 | 14 | @NgModule({ 15 | imports: [SharedModule, RouterModule.forChild(accountState)], 16 | declarations: [ 17 | ActivateComponent, 18 | RegisterComponent, 19 | PasswordComponent, 20 | PasswordStrengthBarComponent, 21 | PasswordResetInitComponent, 22 | PasswordResetFinishComponent, 23 | SettingsComponent, 24 | ], 25 | }) 26 | export class AccountModule {} 27 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/notification.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpInterceptor, HttpRequest, HttpResponse, HttpHandler, HttpEvent } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { tap } from 'rxjs/operators'; 5 | 6 | import { AlertService } from 'app/core/util/alert.service'; 7 | 8 | @Injectable() 9 | export class NotificationInterceptor implements HttpInterceptor { 10 | constructor(private alertService: AlertService) {} 11 | 12 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 13 | return next.handle(request).pipe( 14 | tap((event: HttpEvent) => { 15 | if (event instanceof HttpResponse) { 16 | let alert: string | null = null; 17 | 18 | for (const headerKey of event.headers.keys()) { 19 | if (headerKey.toLowerCase().endsWith('app-alert')) { 20 | alert = event.headers.get(headerKey); 21 | } 22 | } 23 | 24 | if (alert) { 25 | this.alertService.addAlert({ 26 | type: 'success', 27 | message: alert, 28 | }); 29 | } 30 | } 31 | }) 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/sort/sort.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, EventEmitter, Input, Output } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[jhiSort]', 5 | }) 6 | export class SortDirective { 7 | @Input() 8 | get predicate(): T | undefined { 9 | return this._predicate; 10 | } 11 | set predicate(predicate: T | undefined) { 12 | this._predicate = predicate; 13 | this.predicateChange.emit(predicate); 14 | } 15 | 16 | @Input() 17 | get ascending(): boolean | undefined { 18 | return this._ascending; 19 | } 20 | set ascending(ascending: boolean | undefined) { 21 | this._ascending = ascending; 22 | this.ascendingChange.emit(ascending); 23 | } 24 | 25 | @Input() callback?: () => void; 26 | 27 | @Output() predicateChange = new EventEmitter(); 28 | @Output() ascendingChange = new EventEmitter(); 29 | 30 | private _predicate?: T; 31 | private _ascending?: boolean; 32 | 33 | sort(field?: T): void { 34 | if (String(this.predicate) !== '_score') { 35 | this.ascending = field !== this.predicate ? true : !this.ascending; 36 | this.predicate = field; 37 | this.predicateChange.emit(field); 38 | this.ascendingChange.emit(this.ascending); 39 | this.callback?.(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/configuration/configuration.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 7 | import { Bean, Beans, ConfigProps, Env, PropertySource } from './configuration.model'; 8 | 9 | @Injectable({ providedIn: 'root' }) 10 | export class ConfigurationService { 11 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 12 | 13 | getBeans(): Observable { 14 | return this.http.get(this.applicationConfigService.getEndpointFor('management/configprops')).pipe( 15 | map(configProps => 16 | Object.values( 17 | Object.values(configProps.contexts) 18 | .map(context => context.beans) 19 | .reduce((allBeans: Beans, contextBeans: Beans) => ({ ...allBeans, ...contextBeans })) 20 | ) 21 | ) 22 | ); 23 | } 24 | 25 | getPropertySources(): Observable { 26 | return this.http.get(this.applicationConfigService.getEndpointFor('management/env')).pipe(map(env => env.propertySources)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/src/module/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AuthService } from '../service/auth.service'; 3 | import { UserModule } from '../module/user.module'; 4 | import { PassportModule } from '@nestjs/passport'; 5 | import { JwtModule } from '@nestjs/jwt'; 6 | import { JwtStrategy } from '../security/passport.jwt.strategy'; 7 | import { UserJWTController } from '../web/rest/user.jwt.controller'; 8 | import { config } from '../config'; 9 | import { TypeOrmModule } from '@nestjs/typeorm'; 10 | import { AuthorityRepository } from '../repository/authority.repository'; 11 | 12 | import { PublicUserController } from '../web/rest/public.user.controller'; 13 | import { AccountController } from '../web/rest/account.controller'; 14 | 15 | @Module({ 16 | imports: [ 17 | TypeOrmModule.forFeature([AuthorityRepository]), 18 | UserModule, 19 | PassportModule, 20 | JwtModule.register({ 21 | secret: config['jhipster.security.authentication.jwt.base64-secret'], 22 | signOptions: { expiresIn: '300s' }, 23 | }), 24 | ], 25 | controllers: [UserJWTController, PublicUserController, AccountController], 26 | providers: [AuthService, JwtStrategy], 27 | exports: [AuthService], 28 | }) 29 | export class AuthModule {} 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/modal/health-modal.component.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /src/main/webapp/content/scss/_bootstrap-variables.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap overrides https://getbootstrap.com/docs/4.0/getting-started/theming/ 3 | * All values defined in bootstrap source 4 | * https://github.com/twbs/bootstrap/blob/v4-dev/scss/_variables.scss can be overwritten here 5 | * Make sure not to add !default to values here 6 | */ 7 | 8 | // Colors: 9 | // Grayscale and brand colors for use across Bootstrap. 10 | 11 | $primary: #3e8acc; 12 | $success: #28a745; 13 | $info: #17a2b8; 14 | $warning: #ffc107; 15 | $danger: #dc3545; 16 | 17 | // Options: 18 | // Quickly modify global styling by enabling or disabling optional features. 19 | $enable-rounded: true; 20 | $enable-shadows: false; 21 | $enable-gradients: false; 22 | $enable-transitions: true; 23 | $enable-hover-media-query: false; 24 | $enable-grid-classes: true; 25 | $enable-print-styles: true; 26 | 27 | // Components: 28 | // Define common padding and border radius sizes and more. 29 | 30 | $border-radius: 0.15rem; 31 | $border-radius-lg: 0.125rem; 32 | $border-radius-sm: 0.1rem; 33 | 34 | // Body: 35 | // Settings for the `` element. 36 | 37 | $body-bg: #e4e5e6; 38 | 39 | // Typography: 40 | // Font, line-height, and color for body text, headings, and more. 41 | 42 | $font-size-base: 1rem; 43 | 44 | $dropdown-link-hover-color: white; 45 | $dropdown-link-hover-bg: #343a40; 46 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/pagination/item-count.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | /** 4 | * A component that will take care of item count statistics of a pagination. 5 | */ 6 | @Component({ 7 | selector: 'jhi-item-count', 8 | template: `
Showing {{ first }} - {{ second }} of {{ total }} items.
`, 9 | }) 10 | export class ItemCountComponent { 11 | /** 12 | * @param params Contains parameters for component: 13 | * page Current page number 14 | * totalItems Total number of items 15 | * itemsPerPage Number of items per page 16 | */ 17 | @Input() set params(params: { page?: number; totalItems?: number; itemsPerPage?: number }) { 18 | if (params.page !== undefined && params.totalItems !== undefined && params.itemsPerPage !== undefined) { 19 | this.first = (params.page - 1) * params.itemsPerPage === 0 ? 1 : (params.page - 1) * params.itemsPerPage + 1; 20 | this.second = params.page * params.itemsPerPage < params.totalItems ? params.page * params.itemsPerPage : params.totalItems; 21 | } else { 22 | this.first = undefined; 23 | this.second = undefined; 24 | } 25 | this.total = params.totalItems; 26 | } 27 | 28 | first?: number; 29 | second?: number; 30 | total?: number; 31 | } 32 | -------------------------------------------------------------------------------- /server/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { AuthModule } from './module/auth.module'; 4 | import { ormConfig } from './orm.config'; 5 | import { config } from './config'; 6 | import { ServeStaticModule } from '@nestjs/serve-static'; 7 | // jhipster-needle-add-entity-module-to-main-import - JHipster will import entity modules here, do not remove 8 | // jhipster-needle-add-controller-module-to-main-import - JHipster will import controller modules here, do not remove 9 | // jhipster-needle-add-service-module-to-main-import - JHipster will import service modules here, do not remove 10 | 11 | @Module({ 12 | imports: [ 13 | TypeOrmModule.forRootAsync({ useFactory: ormConfig }), 14 | ServeStaticModule.forRoot({ 15 | rootPath: config.getClientPath(), 16 | }), 17 | AuthModule, 18 | // jhipster-needle-add-entity-module-to-main - JHipster will add entity modules here, do not remove 19 | ], 20 | controllers: [ 21 | // jhipster-needle-add-controller-module-to-main - JHipster will add controller modules here, do not remove 22 | ], 23 | providers: [ 24 | // jhipster-needle-add-service-module-to-main - JHipster will add service modules here, do not remove 25 | ], 26 | }) 27 | export class AppModule {} 28 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/config/application-config.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ApplicationConfigService } from './application-config.service'; 4 | 5 | describe('ApplicationConfigService', () => { 6 | let service: ApplicationConfigService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(ApplicationConfigService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | 17 | describe('without prefix', () => { 18 | it('should return correctly', () => { 19 | expect(service.getEndpointFor('api')).toEqual('api'); 20 | }); 21 | 22 | it('should return correctly when passing microservice', () => { 23 | expect(service.getEndpointFor('api', 'microservice')).toEqual('services/microservice/api'); 24 | }); 25 | }); 26 | 27 | describe('with prefix', () => { 28 | beforeEach(() => { 29 | service.setEndpointPrefix('prefix/'); 30 | }); 31 | 32 | it('should return correctly', () => { 33 | expect(service.getEndpointFor('api')).toEqual('prefix/api'); 34 | }); 35 | 36 | it('should return correctly when passing microservice', () => { 37 | expect(service.getEndpointFor('api', 'microservice')).toEqual('prefix/services/microservice/api'); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; 4 | import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; 5 | 6 | import { ApplicationConfigService } from '../config/application-config.service'; 7 | 8 | @Injectable() 9 | export class AuthInterceptor implements HttpInterceptor { 10 | constructor( 11 | private localStorage: LocalStorageService, 12 | private sessionStorage: SessionStorageService, 13 | private applicationConfigService: ApplicationConfigService 14 | ) {} 15 | 16 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 17 | const serverApiUrl = this.applicationConfigService.getEndpointFor(''); 18 | if (!request.url || (request.url.startsWith('http') && !(serverApiUrl && request.url.startsWith(serverApiUrl)))) { 19 | return next.handle(request); 20 | } 21 | 22 | const token: string | null = this.localStorage.retrieve('authenticationToken') ?? this.sessionStorage.retrieve('authenticationToken'); 23 | if (token) { 24 | request = request.clone({ 25 | setHeaders: { 26 | Authorization: `Bearer ${token}`, 27 | }, 28 | }); 29 | } 30 | return next.handle(request); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/webapp/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { errorRoute } from './layouts/error/error.route'; 4 | import { navbarRoute } from './layouts/navbar/navbar.route'; 5 | import { DEBUG_INFO_ENABLED } from 'app/app.constants'; 6 | import { Authority } from 'app/config/authority.constants'; 7 | 8 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 9 | 10 | const LAYOUT_ROUTES = [navbarRoute, ...errorRoute]; 11 | 12 | @NgModule({ 13 | imports: [ 14 | RouterModule.forRoot( 15 | [ 16 | { 17 | path: 'admin', 18 | data: { 19 | authorities: [Authority.ADMIN], 20 | }, 21 | canActivate: [UserRouteAccessService], 22 | loadChildren: () => import('./admin/admin-routing.module').then(m => m.AdminRoutingModule), 23 | }, 24 | { 25 | path: 'account', 26 | loadChildren: () => import('./account/account.module').then(m => m.AccountModule), 27 | }, 28 | { 29 | path: 'login', 30 | loadChildren: () => import('./login/login.module').then(m => m.LoginModule), 31 | }, 32 | ...LAYOUT_ROUTES, 33 | ], 34 | { enableTracing: DEBUG_INFO_ENABLED } 35 | ), 36 | ], 37 | exports: [RouterModule], 38 | }) 39 | export class AppRoutingModule {} 40 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/configuration/configuration.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { ConfigurationService } from './configuration.service'; 4 | import { Bean, PropertySource } from './configuration.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-configuration', 8 | templateUrl: './configuration.component.html', 9 | }) 10 | export class ConfigurationComponent implements OnInit { 11 | allBeans!: Bean[]; 12 | beans: Bean[] = []; 13 | beansFilter = ''; 14 | beansAscending = true; 15 | propertySources: PropertySource[] = []; 16 | 17 | constructor(private configurationService: ConfigurationService) {} 18 | 19 | ngOnInit(): void { 20 | this.configurationService.getBeans().subscribe(beans => { 21 | this.allBeans = beans; 22 | this.filterAndSortBeans(); 23 | }); 24 | 25 | this.configurationService.getPropertySources().subscribe(propertySources => (this.propertySources = propertySources)); 26 | } 27 | 28 | filterAndSortBeans(): void { 29 | const beansAscendingValue = this.beansAscending ? -1 : 1; 30 | const beansAscendingValueReverse = this.beansAscending ? 1 : -1; 31 | this.beans = this.allBeans 32 | .filter(bean => !this.beansFilter || bean.prefix.toLowerCase().includes(this.beansFilter.toLowerCase())) 33 | .sort((a, b) => (a.prefix < b.prefix ? beansAscendingValue : beansAscendingValueReverse)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; 2 | 3 | import { ProcessMetrics } from 'app/admin/metrics/metrics.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-metrics-system', 7 | templateUrl: './metrics-system.component.html', 8 | changeDetection: ChangeDetectionStrategy.OnPush, 9 | }) 10 | export class MetricsSystemComponent { 11 | /** 12 | * object containing thread related metrics 13 | */ 14 | @Input() systemMetrics?: ProcessMetrics; 15 | 16 | /** 17 | * boolean field saying if the metrics are in the process of being updated 18 | */ 19 | @Input() updating?: boolean; 20 | 21 | convertMillisecondsToDuration(ms: number): string { 22 | const times = { 23 | year: 31557600000, 24 | month: 2629746000, 25 | day: 86400000, 26 | hour: 3600000, 27 | minute: 60000, 28 | second: 1000, 29 | }; 30 | let timeString = ''; 31 | for (const [key, value] of Object.entries(times)) { 32 | if (Math.floor(ms / value) > 0) { 33 | let plural = ''; 34 | if (Math.floor(ms / value) > 1) { 35 | plural = 's'; 36 | } 37 | timeString += Math.floor(ms / value).toString() + ' ' + key.toString() + plural + ' '; 38 | ms = ms - value * Math.floor(ms / value); 39 | } 40 | } 41 | return timeString; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { tap } from 'rxjs/operators'; 5 | import { Router } from '@angular/router'; 6 | 7 | import { LoginService } from 'app/login/login.service'; 8 | import { StateStorageService } from 'app/core/auth/state-storage.service'; 9 | import { AccountService } from 'app/core/auth/account.service'; 10 | 11 | @Injectable() 12 | export class AuthExpiredInterceptor implements HttpInterceptor { 13 | constructor( 14 | private loginService: LoginService, 15 | private stateStorageService: StateStorageService, 16 | private router: Router, 17 | private accountService: AccountService 18 | ) {} 19 | 20 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 21 | return next.handle(request).pipe( 22 | tap({ 23 | error: (err: HttpErrorResponse) => { 24 | if (err.status === 401 && err.url && !err.url.includes('api/account') && this.accountService.isAuthenticated()) { 25 | this.stateStorageService.storeUrl(this.router.routerState.snapshot.url); 26 | this.loginService.logout(); 27 | this.router.navigate(['/login']); 28 | } 29 | }, 30 | }) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/metrics.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; 2 | import { combineLatest } from 'rxjs'; 3 | 4 | import { MetricsService } from './metrics.service'; 5 | import { Metrics, Thread } from './metrics.model'; 6 | 7 | @Component({ 8 | selector: 'jhi-metrics', 9 | templateUrl: './metrics.component.html', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | }) 12 | export class MetricsComponent implements OnInit { 13 | metrics?: Metrics; 14 | threads?: Thread[]; 15 | updatingMetrics = true; 16 | 17 | constructor(private metricsService: MetricsService, private changeDetector: ChangeDetectorRef) {} 18 | 19 | ngOnInit(): void { 20 | this.refresh(); 21 | } 22 | 23 | refresh(): void { 24 | this.updatingMetrics = true; 25 | combineLatest([this.metricsService.getMetrics(), this.metricsService.threadDump()]).subscribe(([metrics, threadDump]) => { 26 | this.metrics = metrics; 27 | this.threads = threadDump.threads; 28 | this.updatingMetrics = false; 29 | this.changeDetector.markForCheck(); 30 | }); 31 | } 32 | 33 | metricsKeyExists(key: keyof Metrics): boolean { 34 | return Boolean(this.metrics?.[key]); 35 | } 36 | 37 | metricsKeyExistsAndObjectNotEmpty(key: keyof Metrics): boolean { 38 | return Boolean(this.metrics?.[key] && JSON.stringify(this.metrics[key]) !== '{}'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { SharedLibsModule } from './shared-libs.module'; 3 | import { AlertComponent } from './alert/alert.component'; 4 | import { AlertErrorComponent } from './alert/alert-error.component'; 5 | import { HasAnyAuthorityDirective } from './auth/has-any-authority.directive'; 6 | import { DurationPipe } from './date/duration.pipe'; 7 | import { FormatMediumDatetimePipe } from './date/format-medium-datetime.pipe'; 8 | import { FormatMediumDatePipe } from './date/format-medium-date.pipe'; 9 | import { SortByDirective } from './sort/sort-by.directive'; 10 | import { SortDirective } from './sort/sort.directive'; 11 | import { ItemCountComponent } from './pagination/item-count.component'; 12 | 13 | @NgModule({ 14 | imports: [SharedLibsModule], 15 | declarations: [ 16 | AlertComponent, 17 | AlertErrorComponent, 18 | HasAnyAuthorityDirective, 19 | DurationPipe, 20 | FormatMediumDatetimePipe, 21 | FormatMediumDatePipe, 22 | SortByDirective, 23 | SortDirective, 24 | ItemCountComponent, 25 | ], 26 | exports: [ 27 | SharedLibsModule, 28 | AlertComponent, 29 | AlertErrorComponent, 30 | HasAnyAuthorityDirective, 31 | DurationPipe, 32 | FormatMediumDatetimePipe, 33 | FormatMediumDatePipe, 34 | SortByDirective, 35 | SortDirective, 36 | ItemCountComponent, 37 | ], 38 | }) 39 | export class SharedModule {} 40 | -------------------------------------------------------------------------------- /src/main/webapp/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found 6 | 7 | 8 | 52 | 53 | 54 |

Page Not Found

55 |

Sorry, but the page you were trying to view does not exist.

56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { HttpErrorResponse } from '@angular/common/http'; 3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; 4 | 5 | import { HealthService } from './health.service'; 6 | import { Health, HealthDetails, HealthStatus } from './health.model'; 7 | import { HealthModalComponent } from './modal/health-modal.component'; 8 | 9 | @Component({ 10 | selector: 'jhi-health', 11 | templateUrl: './health.component.html', 12 | }) 13 | export class HealthComponent implements OnInit { 14 | health?: Health; 15 | 16 | constructor(private modalService: NgbModal, private healthService: HealthService) {} 17 | 18 | ngOnInit(): void { 19 | this.refresh(); 20 | } 21 | 22 | getBadgeClass(statusState: HealthStatus): string { 23 | if (statusState === 'UP') { 24 | return 'badge-success'; 25 | } else { 26 | return 'badge-danger'; 27 | } 28 | } 29 | 30 | refresh(): void { 31 | this.healthService.checkHealth().subscribe( 32 | health => (this.health = health), 33 | (error: HttpErrorResponse) => { 34 | if (error.status === 503) { 35 | this.health = error.error; 36 | } 37 | } 38 | ); 39 | } 40 | 41 | showHealth(health: { key: string; value: HealthDetails }): void { 42 | const modalRef = this.modalService.open(HealthModalComponent); 43 | modalRef.componentInstance.health = health; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/font-awesome-icons.ts: -------------------------------------------------------------------------------- 1 | import { 2 | faArrowLeft, 3 | faAsterisk, 4 | faBan, 5 | faBars, 6 | faBell, 7 | faBook, 8 | faCalendarAlt, 9 | faCheck, 10 | faCloud, 11 | faCogs, 12 | faDatabase, 13 | faEye, 14 | faFlag, 15 | faHeart, 16 | faHome, 17 | faList, 18 | faLock, 19 | faPencilAlt, 20 | faPlus, 21 | faRoad, 22 | faSave, 23 | faSearch, 24 | faSignOutAlt, 25 | faSignInAlt, 26 | faSort, 27 | faSortDown, 28 | faSortUp, 29 | faSync, 30 | faTachometerAlt, 31 | faTasks, 32 | faThList, 33 | faTimes, 34 | faTrashAlt, 35 | faUser, 36 | faUserPlus, 37 | faUsers, 38 | faUsersCog, 39 | faWrench, 40 | // jhipster-needle-add-icon-import 41 | } from '@fortawesome/free-solid-svg-icons'; 42 | 43 | export const fontAwesomeIcons = [ 44 | faArrowLeft, 45 | faAsterisk, 46 | faBan, 47 | faBars, 48 | faBell, 49 | faBook, 50 | faCalendarAlt, 51 | faCheck, 52 | faCloud, 53 | faCogs, 54 | faDatabase, 55 | faEye, 56 | faFlag, 57 | faHeart, 58 | faHome, 59 | faList, 60 | faLock, 61 | faPencilAlt, 62 | faPlus, 63 | faRoad, 64 | faSave, 65 | faSearch, 66 | faSignOutAlt, 67 | faSignInAlt, 68 | faSort, 69 | faSortDown, 70 | faSortUp, 71 | faSync, 72 | faTachometerAlt, 73 | faTasks, 74 | faThList, 75 | faTimes, 76 | faTrashAlt, 77 | faUser, 78 | faUserPlus, 79 | faUsers, 80 | faUsersCog, 81 | faWrench, 82 | // jhipster-needle-add-icon-import 83 | ]; 84 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert.component.spec.ts: -------------------------------------------------------------------------------- 1 | jest.mock('app/core/util/alert.service'); 2 | 3 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 4 | 5 | import { AlertService } from 'app/core/util/alert.service'; 6 | 7 | import { AlertComponent } from './alert.component'; 8 | 9 | describe('Component Tests', () => { 10 | describe('Alert Component', () => { 11 | let comp: AlertComponent; 12 | let fixture: ComponentFixture; 13 | let mockAlertService: AlertService; 14 | 15 | beforeEach( 16 | waitForAsync(() => { 17 | TestBed.configureTestingModule({ 18 | declarations: [AlertComponent], 19 | providers: [AlertService], 20 | }) 21 | .overrideTemplate(AlertComponent, '') 22 | .compileComponents(); 23 | }) 24 | ); 25 | 26 | beforeEach(() => { 27 | fixture = TestBed.createComponent(AlertComponent); 28 | comp = fixture.componentInstance; 29 | mockAlertService = TestBed.inject(AlertService); 30 | }); 31 | 32 | it('Should call alertService.get on init', () => { 33 | // WHEN 34 | comp.ngOnInit(); 35 | 36 | // THEN 37 | expect(mockAlertService.get).toHaveBeenCalled(); 38 | }); 39 | 40 | it('Should call alertService.clear on destroy', () => { 41 | // WHEN 42 | comp.ngOnDestroy(); 43 | 44 | // THEN 45 | expect(mockAlertService.clear).toHaveBeenCalled(); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/auth/user-route-access.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, isDevMode } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | import { AccountService } from 'app/core/auth/account.service'; 7 | import { StateStorageService } from './state-storage.service'; 8 | 9 | @Injectable({ providedIn: 'root' }) 10 | export class UserRouteAccessService implements CanActivate { 11 | constructor(private router: Router, private accountService: AccountService, private stateStorageService: StateStorageService) {} 12 | 13 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { 14 | return this.accountService.identity().pipe( 15 | map(account => { 16 | if (account) { 17 | const authorities = route.data['authorities']; 18 | 19 | if (!authorities || authorities.length === 0 || this.accountService.hasAnyAuthority(authorities)) { 20 | return true; 21 | } 22 | 23 | if (isDevMode()) { 24 | console.error('User has not any of required authorities: ', authorities); 25 | } 26 | this.router.navigate(['accessdenied']); 27 | return false; 28 | } 29 | 30 | this.stateStorageService.storeUrl(state.url); 31 | this.router.navigate(['/login']); 32 | return false; 33 | }) 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/parse-links.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { inject, TestBed } from '@angular/core/testing'; 2 | 3 | import { ParseLinks } from './parse-links.service'; 4 | 5 | describe('Parse links service test', () => { 6 | describe('Parse Links Service Test', () => { 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | providers: [ParseLinks], 10 | }); 11 | }); 12 | 13 | it('should throw an error when passed an empty string', inject([ParseLinks], (service: ParseLinks) => { 14 | expect(function () { 15 | service.parse(''); 16 | }).toThrow(new Error('input must not be of zero length')); 17 | })); 18 | 19 | it('should throw an error when passed without comma', inject([ParseLinks], (service: ParseLinks) => { 20 | expect(function () { 21 | service.parse('test'); 22 | }).toThrow(new Error('section could not be split on ";"')); 23 | })); 24 | 25 | it('should throw an error when passed without semicolon', inject([ParseLinks], (service: ParseLinks) => { 26 | expect(function () { 27 | service.parse('test,test2'); 28 | }).toThrow(new Error('section could not be split on ";"')); 29 | })); 30 | 31 | it('should return links when headers are passed', inject([ParseLinks], (service: ParseLinks) => { 32 | const links = { last: 0, first: 0 }; 33 | expect(service.parse(' ; rel="last",; rel="first"')).toEqual(links); 34 | })); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/pagination/item-count.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ItemCountComponent } from './item-count.component'; 4 | 5 | describe('ItemCountComponent test', () => { 6 | let comp: ItemCountComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach( 10 | waitForAsync(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ItemCountComponent], 13 | }).compileComponents(); 14 | }) 15 | ); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(ItemCountComponent); 19 | comp = fixture.componentInstance; 20 | }); 21 | 22 | describe('UI logic tests', () => { 23 | it('should initialize with undefined', () => { 24 | expect(comp.first).toBeUndefined(); 25 | expect(comp.second).toBeUndefined(); 26 | expect(comp.total).toBeUndefined(); 27 | }); 28 | 29 | it('should change the content on page change', () => { 30 | // GIVEN 31 | comp.params = { page: 1, totalItems: 100, itemsPerPage: 10 }; 32 | 33 | // THEN 34 | expect(comp.first).toBe(1); 35 | expect(comp.second).toBe(10); 36 | expect(comp.total).toBe(100); 37 | 38 | // GIVEN 39 | comp.params = { page: 2, totalItems: 100, itemsPerPage: 10 }; 40 | 41 | // THEN 42 | expect(comp.first).toBe(11); 43 | expect(comp.second).toBe(20); 44 | expect(comp.total).toBe(100); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/admin-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | /* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */ 4 | 5 | @NgModule({ 6 | imports: [ 7 | /* jhipster-needle-add-admin-module - JHipster will add admin modules here */ 8 | RouterModule.forChild([ 9 | { 10 | path: 'user-management', 11 | loadChildren: () => import('./user-management/user-management.module').then(m => m.UserManagementModule), 12 | data: { 13 | pageTitle: 'Users', 14 | }, 15 | }, 16 | { 17 | path: 'docs', 18 | loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule), 19 | }, 20 | { 21 | path: 'configuration', 22 | loadChildren: () => import('./configuration/configuration.module').then(m => m.ConfigurationModule), 23 | }, 24 | { 25 | path: 'health', 26 | loadChildren: () => import('./health/health.module').then(m => m.HealthModule), 27 | }, 28 | { 29 | path: 'logs', 30 | loadChildren: () => import('./logs/logs.module').then(m => m.LogsModule), 31 | }, 32 | { 33 | path: 'metrics', 34 | loadChildren: () => import('./metrics/metrics.module').then(m => m.MetricsModule), 35 | }, 36 | /* jhipster-needle-add-admin-route - JHipster will add admin routes here */ 37 | ]), 38 | ], 39 | }) 40 | export class AdminRoutingModule {} 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/main/main.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Title } from '@angular/platform-browser'; 3 | import { Router, ActivatedRouteSnapshot, NavigationEnd, NavigationError } from '@angular/router'; 4 | 5 | import { AccountService } from 'app/core/auth/account.service'; 6 | 7 | @Component({ 8 | selector: 'jhi-main', 9 | templateUrl: './main.component.html', 10 | }) 11 | export class MainComponent implements OnInit { 12 | constructor(private accountService: AccountService, private titleService: Title, private router: Router) {} 13 | 14 | ngOnInit(): void { 15 | // try to log in automatically 16 | this.accountService.identity().subscribe(); 17 | 18 | this.router.events.subscribe(event => { 19 | if (event instanceof NavigationEnd) { 20 | this.updateTitle(); 21 | } 22 | if (event instanceof NavigationError && event.error.status === 404) { 23 | this.router.navigate(['/404']); 24 | } 25 | }); 26 | } 27 | 28 | private getPageTitle(routeSnapshot: ActivatedRouteSnapshot): string { 29 | let title: string = routeSnapshot.data['pageTitle'] ?? ''; 30 | if (routeSnapshot.firstChild) { 31 | title = this.getPageTitle(routeSnapshot.firstChild) || title; 32 | } 33 | return title; 34 | } 35 | 36 | private updateTitle(): void { 37 | let pageTitle = this.getPageTitle(this.router.routerState.snapshot.root); 38 | if (!pageTitle) { 39 | pageTitle = 'Gen'; 40 | } 41 | this.titleService.setTitle(pageTitle); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | Health Checks 4 | 5 | 6 |

7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 27 | 36 | 37 | 38 |
Service NameStatusDetails
20 | {{ componentHealth.key }} 21 | 23 | 24 | {{ componentHealth.value!.status }} 25 | 26 | 28 | 33 | 34 | 35 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/parse-links.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | /** 4 | * An utility service for link parsing. 5 | */ 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class ParseLinks { 10 | /** 11 | * Method to parse the links 12 | */ 13 | parse(header: string): { [key: string]: number } { 14 | if (header.length === 0) { 15 | throw new Error('input must not be of zero length'); 16 | } 17 | 18 | // Split parts by comma 19 | const parts: string[] = header.split(','); 20 | const links: { [key: string]: number } = {}; 21 | 22 | // Parse each part into a named link 23 | parts.forEach(p => { 24 | const section: string[] = p.split(';'); 25 | 26 | if (section.length !== 2) { 27 | throw new Error('section could not be split on ";"'); 28 | } 29 | 30 | const url: string = section[0].replace(/<(.*)>/, '$1').trim(); 31 | const queryString: { [key: string]: string | undefined } = {}; 32 | 33 | url.replace( 34 | new RegExp('([^?=&]+)(=([^&]*))?', 'g'), 35 | ($0: string, $1: string | undefined, $2: string | undefined, $3: string | undefined) => { 36 | if ($1 !== undefined) { 37 | queryString[$1] = $3; 38 | } 39 | return $3 ?? ''; 40 | } 41 | ); 42 | 43 | if (queryString.page !== undefined) { 44 | const name: string = section[1].replace(/rel="(.*)"/, '$1').trim(); 45 | links[name] = parseInt(queryString.page, 10); 46 | } 47 | }); 48 | return links; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/javascript/protractor.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | allScriptsTimeout: 20000, 3 | 4 | specs: [ 5 | './e2e/account/**/*.spec.ts', 6 | './e2e/admin/**/*.spec.ts', 7 | './e2e/entities/**/*.spec.ts', 8 | /* jhipster-needle-add-protractor-tests - JHipster will add protractors tests here */ 9 | ], 10 | 11 | capabilities: { 12 | browserName: 'chrome', 13 | chromeOptions: { 14 | args: process.env.JHI_E2E_HEADLESS 15 | ? ['--headless', '--disable-gpu', '--window-size=800,600', '--no-sandbox', '--disable-dev-shm-usage'] 16 | : ['--disable-gpu', '--window-size=800,600'], 17 | }, 18 | }, 19 | 20 | directConnect: true, 21 | 22 | baseUrl: 'http://localhost:8081/', 23 | 24 | framework: 'mocha', 25 | 26 | SELENIUM_PROMISE_MANAGER: false, 27 | 28 | mochaOpts: { 29 | reporter: 'spec', 30 | slow: 3000, 31 | ui: 'bdd', 32 | timeout: 720000, 33 | }, 34 | 35 | beforeLaunch: function () { 36 | require('ts-node').register({ 37 | project: 'tsconfig.e2e.json', 38 | }); 39 | }, 40 | 41 | onPrepare: function () { 42 | browser.driver.manage().window().setSize(1280, 1024); 43 | // Disable animations 44 | // @ts-ignore 45 | browser.executeScript('document.body.className += " notransition";'); 46 | const chai = require('chai'); 47 | const chaiAsPromised = require('chai-as-promised'); 48 | chai.use(chaiAsPromised); 49 | const chaiString = require('chai-string'); 50 | chai.use(chaiString); 51 | // @ts-ignore 52 | global.chai = chai; 53 | }, 54 | 55 | useAllAngular2AppRoots: true, 56 | }; 57 | -------------------------------------------------------------------------------- /jest.conf.js: -------------------------------------------------------------------------------- 1 | const tsconfig = require('./tsconfig.json'); 2 | 3 | module.exports = { 4 | setupFiles: ['jest-date-mock'], 5 | cacheDirectory: '/target/jest-cache', 6 | coverageDirectory: '/target/test-results/', 7 | moduleNameMapper: mapTypescriptAliasToJestAlias(), 8 | reporters: ['default', ['jest-junit', { outputDirectory: './target/test-results/', outputName: 'TESTS-results-jest.xml' }]], 9 | testResultsProcessor: 'jest-sonar-reporter', 10 | testMatch: ['/src/main/webapp/app/**/@(*.)@(spec.ts)'], 11 | testURL: 'http://localhost/', 12 | }; 13 | 14 | function mapTypescriptAliasToJestAlias(alias = {}) { 15 | const jestAliases = { ...alias }; 16 | if (!tsconfig.compilerOptions.paths) { 17 | return jestAliases; 18 | } 19 | Object.entries(tsconfig.compilerOptions.paths) 20 | .filter(([key, value]) => { 21 | // use Typescript alias in Jest only if this has value 22 | if (value.length) { 23 | return true; 24 | } 25 | return false; 26 | }) 27 | .map(([key, value]) => { 28 | // if Typescript alias ends with /* then in Jest: 29 | // - alias key must end with /(.*) 30 | // - alias value must end with /$1 31 | const regexToReplace = /(.*)\/\*$/; 32 | const aliasKey = key.replace(regexToReplace, '$1/(.*)'); 33 | const aliasValue = value[0].replace(regexToReplace, '$1/$$1'); 34 | return [aliasKey, `/${aliasValue}`]; 35 | }) 36 | .reduce((aliases, [key, value]) => { 37 | aliases[key] = value; 38 | return aliases; 39 | }, jestAliases); 40 | return jestAliases; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/logs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { Log, LoggersResponse, Level } from './log.model'; 4 | import { LogsService } from './logs.service'; 5 | 6 | @Component({ 7 | selector: 'jhi-logs', 8 | templateUrl: './logs.component.html', 9 | }) 10 | export class LogsComponent implements OnInit { 11 | loggers?: Log[]; 12 | filteredAndOrderedLoggers?: Log[]; 13 | filter = ''; 14 | orderProp: keyof Log = 'name'; 15 | ascending = true; 16 | 17 | constructor(private logsService: LogsService) {} 18 | 19 | ngOnInit(): void { 20 | this.findAndExtractLoggers(); 21 | } 22 | 23 | changeLevel(name: string, level: Level): void { 24 | this.logsService.changeLevel(name, level).subscribe(() => this.findAndExtractLoggers()); 25 | } 26 | 27 | filterAndSort(): void { 28 | this.filteredAndOrderedLoggers = this.loggers!.filter( 29 | logger => !this.filter || logger.name.toLowerCase().includes(this.filter.toLowerCase()) 30 | ).sort((a, b) => { 31 | if (a[this.orderProp] < b[this.orderProp]) { 32 | return this.ascending ? -1 : 1; 33 | } else if (a[this.orderProp] > b[this.orderProp]) { 34 | return this.ascending ? 1 : -1; 35 | } else if (this.orderProp === 'level') { 36 | return a.name < b.name ? -1 : 1; 37 | } 38 | return 0; 39 | }); 40 | } 41 | 42 | private findAndExtractLoggers(): void { 43 | this.logsService.findAll().subscribe((response: LoggersResponse) => { 44 | this.loggers = Object.entries(response.loggers).map(([key, logger]) => new Log(key, logger.effectiveLevel)); 45 | this.filterAndSort(); 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/service/user-management.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpResponse } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { createRequestOption } from 'app/core/request/request-util'; 7 | import { Pagination } from 'app/core/request/request.model'; 8 | import { IUser } from '../user-management.model'; 9 | 10 | @Injectable({ providedIn: 'root' }) 11 | export class UserManagementService { 12 | public resourceUrl = this.applicationConfigService.getEndpointFor('api/admin/users'); 13 | 14 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 15 | 16 | create(user: IUser): Observable { 17 | return this.http.post(this.resourceUrl, user); 18 | } 19 | 20 | update(user: IUser): Observable { 21 | return this.http.put(this.resourceUrl, user); 22 | } 23 | 24 | find(login: string): Observable { 25 | return this.http.get(`${this.resourceUrl}/${login}`); 26 | } 27 | 28 | query(req?: Pagination): Observable> { 29 | const options = createRequestOption(req); 30 | return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); 31 | } 32 | 33 | delete(login: string): Observable<{}> { 34 | return this.http.delete(`${this.resourceUrl}/${login}`); 35 | } 36 | 37 | authorities(): Observable { 38 | return this.http.get(this.applicationConfigService.getEndpointFor('api/authorities')); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /server/src/service/dto/user.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiModelProperty } from '@nestjs/swagger'; 2 | import { IsString, IsEmail } from 'class-validator'; 3 | import { BaseDTO } from './base.dto'; 4 | import { Exclude } from 'class-transformer'; 5 | 6 | /** 7 | * An User DTO object. 8 | */ 9 | export class UserDTO extends BaseDTO { 10 | @ApiModelProperty({ uniqueItems: true, example: 'myuser', description: 'User login' }) 11 | @IsString() 12 | login: string; 13 | 14 | @ApiModelProperty({ example: 'MyUser', description: 'User first name', required: false }) 15 | firstName?: string; 16 | 17 | @ApiModelProperty({ example: 'MyUser', description: 'User last name', required: false }) 18 | lastName?: string; 19 | 20 | @ApiModelProperty({ example: 'myuser@localhost.it', description: 'User email' }) 21 | @IsEmail() 22 | email: string; 23 | 24 | @ApiModelProperty({ example: 'true', description: 'User activation', required: false }) 25 | activated?: boolean; 26 | 27 | @ApiModelProperty({ example: 'en', description: 'User language', required: false }) 28 | langKey?: string; 29 | 30 | @ApiModelProperty({ 31 | isArray: true, 32 | enum: ['ROLE_USER', 'ROLE_ADMIN', 'ROLE_ANONYMOUS'], 33 | description: 'Array of permissions', 34 | required: false, 35 | }) 36 | authorities?: any[]; 37 | 38 | @Exclude() 39 | @ApiModelProperty({ example: 'myuser', description: 'User password' }) 40 | password: string; 41 | 42 | @ApiModelProperty({ example: 'http://my-image-url', description: 'Image url', required: false }) 43 | imageUrl?: string; 44 | 45 | activationKey?: string; 46 | 47 | resetKey?: string; 48 | 49 | resetDate?: Date; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/metrics.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import { SharedModule } from 'app/shared/shared.module'; 5 | import { MetricsComponent } from './metrics.component'; 6 | import { metricsRoute } from './metrics.route'; 7 | import { JvmMemoryComponent } from './blocks/jvm-memory/jvm-memory.component'; 8 | import { JvmThreadsComponent } from './blocks/jvm-threads/jvm-threads.component'; 9 | import { MetricsCacheComponent } from './blocks/metrics-cache/metrics-cache.component'; 10 | import { MetricsDatasourceComponent } from './blocks/metrics-datasource/metrics-datasource.component'; 11 | import { MetricsEndpointsRequestsComponent } from './blocks/metrics-endpoints-requests/metrics-endpoints-requests.component'; 12 | import { MetricsGarbageCollectorComponent } from './blocks/metrics-garbagecollector/metrics-garbagecollector.component'; 13 | import { MetricsModalThreadsComponent } from './blocks/metrics-modal-threads/metrics-modal-threads.component'; 14 | import { MetricsRequestComponent } from './blocks/metrics-request/metrics-request.component'; 15 | import { MetricsSystemComponent } from './blocks/metrics-system/metrics-system.component'; 16 | 17 | @NgModule({ 18 | imports: [SharedModule, RouterModule.forChild([metricsRoute])], 19 | declarations: [ 20 | MetricsComponent, 21 | JvmMemoryComponent, 22 | JvmThreadsComponent, 23 | MetricsCacheComponent, 24 | MetricsDatasourceComponent, 25 | MetricsEndpointsRequestsComponent, 26 | MetricsGarbageCollectorComponent, 27 | MetricsModalThreadsComponent, 28 | MetricsRequestComponent, 29 | MetricsSystemComponent, 30 | ], 31 | }) 32 | export class MetricsModule {} 33 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/user-management.route.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Resolve, ActivatedRouteSnapshot, Routes } from '@angular/router'; 3 | import { Observable, of } from 'rxjs'; 4 | 5 | import { User, IUser } from './user-management.model'; 6 | import { UserManagementService } from './service/user-management.service'; 7 | import { UserManagementComponent } from './list/user-management.component'; 8 | import { UserManagementDetailComponent } from './detail/user-management-detail.component'; 9 | import { UserManagementUpdateComponent } from './update/user-management-update.component'; 10 | 11 | @Injectable({ providedIn: 'root' }) 12 | export class UserManagementResolve implements Resolve { 13 | constructor(private service: UserManagementService) {} 14 | 15 | resolve(route: ActivatedRouteSnapshot): Observable { 16 | const id = route.params['login']; 17 | if (id) { 18 | return this.service.find(id); 19 | } 20 | return of(new User()); 21 | } 22 | } 23 | 24 | export const userManagementRoute: Routes = [ 25 | { 26 | path: '', 27 | component: UserManagementComponent, 28 | data: { 29 | defaultSort: 'id,asc', 30 | }, 31 | }, 32 | { 33 | path: ':login/view', 34 | component: UserManagementDetailComponent, 35 | resolve: { 36 | user: UserManagementResolve, 37 | }, 38 | }, 39 | { 40 | path: 'new', 41 | component: UserManagementUpdateComponent, 42 | resolve: { 43 | user: UserManagementResolve, 44 | }, 45 | }, 46 | { 47 | path: ':login/edit', 48 | component: UserManagementUpdateComponent, 49 | resolve: { 50 | user: UserManagementResolve, 51 | }, 52 | }, 53 | ]; 54 | -------------------------------------------------------------------------------- /server/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import request = require('supertest'); 3 | import { AppModule } from '../src/app.module'; 4 | import { INestApplication } from '@nestjs/common'; 5 | import { UserLoginDTO } from '../src/service/dto/user-login.dto'; 6 | 7 | describe('App', () => { 8 | let app: INestApplication; 9 | 10 | const infoService = { 11 | activeProfiles: 'dev', 12 | 'display-ribbon-on-profiles': 'dev', 13 | }; 14 | const testUserLogin: UserLoginDTO = { 15 | username: 'system', 16 | password: 'system', 17 | rememberMe: true, 18 | }; 19 | 20 | beforeEach(async () => { 21 | const moduleFixture: TestingModule = await Test.createTestingModule({ 22 | imports: [AppModule], 23 | }).compile(); 24 | 25 | app = moduleFixture.createNestApplication(); 26 | await app.init(); 27 | }); 28 | 29 | it('/GET up running info OK', () => 30 | request(app.getHttpServer()) 31 | .get('/management/info') 32 | .expect(200) 33 | .expect(infoService)); 34 | 35 | it('/GET public roles OK', () => 36 | request(app.getHttpServer()) 37 | .get('/api/authorities') 38 | .expect(200)); 39 | 40 | it('/GET public users OK', () => 41 | request(app.getHttpServer()) 42 | .get('/api/users') 43 | .expect(200)); 44 | 45 | it('/POST authenticate get jwt authenticate OK', () => 46 | request(app.getHttpServer()) 47 | .post('/api/authenticate') 48 | .send(testUserLogin) 49 | .expect(201)); 50 | 51 | afterEach(async () => { 52 | await app.close(); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormBuilder, Validators } from '@angular/forms'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { AccountService } from 'app/core/auth/account.service'; 6 | import { Account } from 'app/core/auth/account.model'; 7 | import { PasswordService } from './password.service'; 8 | 9 | @Component({ 10 | selector: 'jhi-password', 11 | templateUrl: './password.component.html', 12 | }) 13 | export class PasswordComponent implements OnInit { 14 | doNotMatch = false; 15 | error = false; 16 | success = false; 17 | account$?: Observable; 18 | passwordForm = this.fb.group({ 19 | currentPassword: ['', [Validators.required]], 20 | newPassword: ['', [Validators.required, Validators.minLength(4), Validators.maxLength(50)]], 21 | confirmPassword: ['', [Validators.required, Validators.minLength(4), Validators.maxLength(50)]], 22 | }); 23 | 24 | constructor(private passwordService: PasswordService, private accountService: AccountService, private fb: FormBuilder) {} 25 | 26 | ngOnInit(): void { 27 | this.account$ = this.accountService.identity(); 28 | } 29 | 30 | changePassword(): void { 31 | this.error = false; 32 | this.success = false; 33 | this.doNotMatch = false; 34 | 35 | const newPassword = this.passwordForm.get(['newPassword'])!.value; 36 | if (newPassword !== this.passwordForm.get(['confirmPassword'])!.value) { 37 | this.doNotMatch = true; 38 | } else { 39 | this.passwordService.save(newPassword, this.passwordForm.get(['currentPassword'])!.value).subscribe( 40 | () => (this.success = true), 41 | () => (this.error = true) 42 | ); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-jhipster": { 3 | "blueprints": [ 4 | { 5 | "name": "generator-jhipster-nodejs", 6 | "version": "2.0.0" 7 | } 8 | ], 9 | "otherModules": [ 10 | { 11 | "name": "generator-jhipster-nodejs", 12 | "version": "2.0.0" 13 | } 14 | ], 15 | "applicationType": "monolith", 16 | "baseName": "gen", 17 | "jhipsterVersion": "7.0.1", 18 | "skipClient": false, 19 | "skipServer": false, 20 | "skipUserManagement": false, 21 | "skipCheckLengthOfIdentifier": false, 22 | "skipFakeData": false, 23 | "jhiPrefix": "jhi", 24 | "entitySuffix": "", 25 | "dtoSuffix": "DTO", 26 | "testFrameworks": [], 27 | "pages": [], 28 | "creationTimestamp": 1617870743335, 29 | "serverPort": "8081", 30 | "packageName": "com.jhipster.node", 31 | "databaseType": "sql", 32 | "devDatabaseType": "sqlite", 33 | "prodDatabaseType": "mysql", 34 | "authenticationType": "jwt", 35 | "clientFramework": "angularX", 36 | "withAdminUi": true, 37 | "clientTheme": "none", 38 | "enableTranslation": false, 39 | "nativeLanguage": "en", 40 | "packageFolder": "com/jhipster/node", 41 | "jwtSecretKey": "Mjk5MDU5MDI1OGM5NGM2NmQxN2NkZmQ0MzEzZmI3OGI5NGM2NjRmYTRmM2VmYjcyNzlmMGIzMzM4YmI4YTQxMDU4YmFkN2U2ZTYxOWE3OWRjOWViNDY3ZTIwYTk2YzFlYzEwZjExMWFiMGUyMGZlYWQ4NzhkNjc0MGM0ZDNiNzk=", 42 | "serviceDiscoveryType": false, 43 | "websocket": false, 44 | "searchEngine": false, 45 | "messageBroker": false, 46 | "enableHibernateCache": false, 47 | "clientPackageManager": "npm", 48 | "clientThemeVariant": "", 49 | "cacheProvider": "ehcache", 50 | "buildTool": "maven", 51 | "reactive": false, 52 | "languages": ["en", "fr"] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/metrics.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | import { of } from 'rxjs'; 4 | 5 | import { MetricsComponent } from './metrics.component'; 6 | import { MetricsService } from './metrics.service'; 7 | 8 | describe('Component Tests', () => { 9 | describe('MetricsComponent', () => { 10 | let comp: MetricsComponent; 11 | let fixture: ComponentFixture; 12 | let service: MetricsService; 13 | 14 | beforeEach( 15 | waitForAsync(() => { 16 | TestBed.configureTestingModule({ 17 | imports: [HttpClientTestingModule], 18 | declarations: [MetricsComponent], 19 | }) 20 | .overrideTemplate(MetricsComponent, '') 21 | .compileComponents(); 22 | }) 23 | ); 24 | 25 | beforeEach(() => { 26 | fixture = TestBed.createComponent(MetricsComponent); 27 | comp = fixture.componentInstance; 28 | service = TestBed.inject(MetricsService); 29 | }); 30 | 31 | describe('refresh', () => { 32 | it('should call refresh on init', () => { 33 | // GIVEN 34 | const response = { 35 | timers: { 36 | service: 'test', 37 | unrelatedKey: 'test', 38 | }, 39 | gauges: { 40 | 'jcache.statistics': { 41 | value: 2, 42 | }, 43 | unrelatedKey: 'test', 44 | }, 45 | }; 46 | spyOn(service, 'getMetrics').and.returnValue(of(response)); 47 | 48 | // WHEN 49 | comp.ngOnInit(); 50 | 51 | // THEN 52 | expect(service.getMetrics).toHaveBeenCalled(); 53 | }); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/settings/settings.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormBuilder, Validators } from '@angular/forms'; 3 | 4 | import { AccountService } from 'app/core/auth/account.service'; 5 | import { Account } from 'app/core/auth/account.model'; 6 | 7 | @Component({ 8 | selector: 'jhi-settings', 9 | templateUrl: './settings.component.html', 10 | }) 11 | export class SettingsComponent implements OnInit { 12 | account!: Account; 13 | success = false; 14 | settingsForm = this.fb.group({ 15 | firstName: [undefined, [Validators.required, Validators.minLength(1), Validators.maxLength(50)]], 16 | lastName: [undefined, [Validators.required, Validators.minLength(1), Validators.maxLength(50)]], 17 | email: [undefined, [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email]], 18 | }); 19 | 20 | constructor(private accountService: AccountService, private fb: FormBuilder) {} 21 | 22 | ngOnInit(): void { 23 | this.accountService.identity().subscribe(account => { 24 | if (account) { 25 | this.settingsForm.patchValue({ 26 | firstName: account.firstName, 27 | lastName: account.lastName, 28 | email: account.email, 29 | }); 30 | 31 | this.account = account; 32 | } 33 | }); 34 | } 35 | 36 | save(): void { 37 | this.success = false; 38 | 39 | this.account.firstName = this.settingsForm.get('firstName')!.value; 40 | this.account.lastName = this.settingsForm.get('lastName')!.value; 41 | this.account.email = this.settingsForm.get('email')!.value; 42 | 43 | this.accountService.save(this.account).subscribe(() => { 44 | this.success = true; 45 | 46 | this.accountService.authenticate(this.account); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpResponse } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { createRequestOption } from 'app/core/request/request-util'; 7 | import { isPresent } from 'app/core/util/operators'; 8 | import { Pagination } from 'app/core/request/request.model'; 9 | import { IUser, getUserIdentifier } from './user.model'; 10 | 11 | @Injectable({ providedIn: 'root' }) 12 | export class UserService { 13 | public resourceUrl = this.applicationConfigService.getEndpointFor('api/users'); 14 | 15 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 16 | 17 | query(req?: Pagination): Observable> { 18 | const options = createRequestOption(req); 19 | return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); 20 | } 21 | 22 | addUserToCollectionIfMissing(userCollection: IUser[], ...usersToCheck: (IUser | null | undefined)[]): IUser[] { 23 | const users: IUser[] = usersToCheck.filter(isPresent); 24 | if (users.length > 0) { 25 | const userCollectionIdentifiers = userCollection.map(userItem => getUserIdentifier(userItem)!); 26 | const usersToAdd = users.filter(userItem => { 27 | const userIdentifier = getUserIdentifier(userItem); 28 | if (userIdentifier == null || userCollectionIdentifiers.includes(userIdentifier)) { 29 | return false; 30 | } 31 | userCollectionIdentifiers.push(userIdentifier); 32 | return true; 33 | }); 34 | return [...usersToAdd, ...userCollection]; 35 | } 36 | return userCollection; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/profile.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { map, shareReplay } from 'rxjs/operators'; 4 | import { Observable } from 'rxjs'; 5 | 6 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 7 | import { ProfileInfo, InfoResponse } from './profile-info.model'; 8 | 9 | @Injectable({ providedIn: 'root' }) 10 | export class ProfileService { 11 | private infoUrl = this.applicationConfigService.getEndpointFor('management/info'); 12 | private profileInfo$?: Observable; 13 | 14 | constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} 15 | 16 | getProfileInfo(): Observable { 17 | if (this.profileInfo$) { 18 | return this.profileInfo$; 19 | } 20 | 21 | this.profileInfo$ = this.http.get(this.infoUrl).pipe( 22 | map((response: InfoResponse) => { 23 | const profileInfo: ProfileInfo = { 24 | activeProfiles: response.activeProfiles, 25 | inProduction: response.activeProfiles?.includes('prod'), 26 | openAPIEnabled: response.activeProfiles?.includes('api-docs'), 27 | }; 28 | if (response.activeProfiles && response['display-ribbon-on-profiles']) { 29 | const displayRibbonOnProfiles = response['display-ribbon-on-profiles'].split(','); 30 | const ribbonProfiles = displayRibbonOnProfiles.filter(profile => response.activeProfiles?.includes(profile)); 31 | if (ribbonProfiles.length > 0) { 32 | profileInfo.ribbonEnv = ribbonProfiles[0]; 33 | } 34 | } 35 | return profileInfo; 36 | }), 37 | shareReplay() 38 | ); 39 | return this.profileInfo$; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/detail/user-management-detail.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

5 | User [{{ user.login }}] 7 |

8 | 9 |
10 |
Login
11 |
12 | {{ user.login }} 13 | Activated 14 | Deactivated 15 |
16 | 17 |
First Name
18 |
{{ user.firstName }}
19 | 20 |
Last Name
21 |
{{ user.lastName }}
22 | 23 |
Email
24 |
{{ user.email }}
25 | 26 |
Created By
27 |
{{ user.createdBy }}
28 | 29 |
Created Date
30 |
{{ user.createdDate | date: 'dd/MM/yy HH:mm' }}
31 | 32 |
Last Modified By
33 |
{{ user.lastModifiedBy }}
34 | 35 |
Last Modified Date
36 |
{{ user.lastModifiedDate | date: 'dd/MM/yy HH:mm' }}
37 | 38 |
Profiles
39 |
40 |
    41 |
  • 42 | {{ authority }} 43 |
  • 44 |
45 |
46 |
47 | 48 | 49 |
50 |
51 |
52 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=gen 2 | sonar.projectName=gen generated by jhipster 3 | sonar.projectVersion=1.0 4 | 5 | sonar.sources=src/main/ 6 | sonar.host.url=http://localhost:9001 7 | 8 | sonar.test.inclusions=src/test/**/*.*, src/main/webapp/app/**/*.spec.ts 9 | sonar.coverage.jacoco.xmlReportPaths=target/site/**/jacoco*.xml 10 | sonar.java.codeCoveragePlugin=jacoco 11 | sonar.junit.reportPaths=target/surefire-reports,target/failsafe-reports 12 | sonar.testExecutionReportPaths=target/test-results/jest/TESTS-results-sonar.xml 13 | sonar.javascript.lcov.reportPaths=target/test-results/lcov.info 14 | 15 | sonar.sourceEncoding=UTF-8 16 | sonar.exclusions=src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, target/classes/static/**/*.* 17 | 18 | sonar.issue.ignore.multicriteria=S3437,S4502,S4684,UndocumentedApi 19 | # Rule https://rules.sonarsource.com/java/RSPEC-3437 is ignored, as a JPA-managed field cannot be transient 20 | sonar.issue.ignore.multicriteria.S3437.resourceKey=src/main/java/**/* 21 | sonar.issue.ignore.multicriteria.S3437.ruleKey=squid:S3437 22 | # Rule https://rules.sonarsource.com/java/RSPEC-1176 is ignored, as we want to follow "clean code" guidelines and classes, methods and arguments names should be self-explanatory 23 | sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey=src/main/java/**/* 24 | sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey=squid:UndocumentedApi 25 | # Rule https://rules.sonarsource.com/java/RSPEC-4502 is ignored, as for JWT tokens we are not subject to CSRF attack 26 | sonar.issue.ignore.multicriteria.S4502.resourceKey=src/main/java/**/* 27 | sonar.issue.ignore.multicriteria.S4502.ruleKey=squid:S4502 28 | # Rule https://rules.sonarsource.com/java/RSPEC-4684 29 | sonar.issue.ignore.multicriteria.S4684.resourceKey=src/main/java/**/* 30 | sonar.issue.ignore.multicriteria.S4684.ruleKey=java:S4684 31 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/auth/has-any-authority.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, TemplateRef, ViewContainerRef, OnDestroy } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { takeUntil } from 'rxjs/operators'; 4 | 5 | import { AccountService } from 'app/core/auth/account.service'; 6 | 7 | /** 8 | * @whatItDoes Conditionally includes an HTML element if current user has any 9 | * of the authorities passed as the `expression`. 10 | * 11 | * @howToUse 12 | * ``` 13 | * ... 14 | * 15 | * ... 16 | * ``` 17 | */ 18 | @Directive({ 19 | selector: '[jhiHasAnyAuthority]', 20 | }) 21 | export class HasAnyAuthorityDirective implements OnDestroy { 22 | private authorities!: string | string[]; 23 | 24 | private readonly destroy$ = new Subject(); 25 | 26 | constructor(private accountService: AccountService, private templateRef: TemplateRef, private viewContainerRef: ViewContainerRef) {} 27 | 28 | @Input() 29 | set jhiHasAnyAuthority(value: string | string[]) { 30 | this.authorities = value; 31 | this.updateView(); 32 | // Get notified each time authentication state changes. 33 | this.accountService 34 | .getAuthenticationState() 35 | .pipe(takeUntil(this.destroy$)) 36 | .subscribe(() => { 37 | this.updateView(); 38 | }); 39 | } 40 | 41 | ngOnDestroy(): void { 42 | this.destroy$.next(); 43 | this.destroy$.complete(); 44 | } 45 | 46 | private updateView(): void { 47 | const hasAnyAuthority = this.accountService.hasAnyAuthority(this.authorities); 48 | this.viewContainerRef.clear(); 49 | if (hasAnyAuthority) { 50 | this.viewContainerRef.createEmbeddedView(this.templateRef); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/sort/sort-by.directive.ts: -------------------------------------------------------------------------------- 1 | import { AfterContentInit, ContentChild, Directive, Host, HostListener, Input, OnDestroy } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { takeUntil } from 'rxjs/operators'; 4 | import { FaIconComponent } from '@fortawesome/angular-fontawesome'; 5 | import { faSort, faSortDown, faSortUp, IconDefinition } from '@fortawesome/free-solid-svg-icons'; 6 | 7 | import { SortDirective } from './sort.directive'; 8 | 9 | @Directive({ 10 | selector: '[jhiSortBy]', 11 | }) 12 | export class SortByDirective implements AfterContentInit, OnDestroy { 13 | @Input() jhiSortBy?: T; 14 | 15 | @ContentChild(FaIconComponent, { static: true }) 16 | iconComponent?: FaIconComponent; 17 | 18 | sortIcon = faSort; 19 | sortAscIcon = faSortUp; 20 | sortDescIcon = faSortDown; 21 | 22 | private readonly destroy$ = new Subject(); 23 | 24 | constructor(@Host() private sort: SortDirective) { 25 | sort.predicateChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.updateIconDefinition()); 26 | sort.ascendingChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.updateIconDefinition()); 27 | } 28 | 29 | @HostListener('click') 30 | onClick(): void { 31 | this.sort.sort(this.jhiSortBy); 32 | } 33 | 34 | ngAfterContentInit(): void { 35 | this.updateIconDefinition(); 36 | } 37 | 38 | ngOnDestroy(): void { 39 | this.destroy$.next(); 40 | this.destroy$.complete(); 41 | } 42 | 43 | private updateIconDefinition(): void { 44 | if (this.iconComponent) { 45 | let icon: IconDefinition = this.sortIcon; 46 | if (this.sort.predicate === this.jhiSortBy) { 47 | icon = this.sort.ascending ? this.sortAscIcon : this.sortDescIcon; 48 | } 49 | this.iconComponent.icon = icon.iconName; 50 | this.iconComponent.render(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; 3 | 4 | import { Thread } from 'app/admin/metrics/metrics.model'; 5 | import { MetricsModalThreadsComponent } from '../metrics-modal-threads/metrics-modal-threads.component'; 6 | 7 | @Component({ 8 | selector: 'jhi-jvm-threads', 9 | templateUrl: './jvm-threads.component.html', 10 | }) 11 | export class JvmThreadsComponent { 12 | threadStats = { 13 | threadDumpAll: 0, 14 | threadDumpRunnable: 0, 15 | threadDumpTimedWaiting: 0, 16 | threadDumpWaiting: 0, 17 | threadDumpBlocked: 0, 18 | }; 19 | 20 | @Input() 21 | set threads(threads: Thread[] | undefined) { 22 | this._threads = threads; 23 | 24 | threads?.forEach(thread => { 25 | if (thread.threadState === 'RUNNABLE') { 26 | this.threadStats.threadDumpRunnable += 1; 27 | } else if (thread.threadState === 'WAITING') { 28 | this.threadStats.threadDumpWaiting += 1; 29 | } else if (thread.threadState === 'TIMED_WAITING') { 30 | this.threadStats.threadDumpTimedWaiting += 1; 31 | } else if (thread.threadState === 'BLOCKED') { 32 | this.threadStats.threadDumpBlocked += 1; 33 | } 34 | }); 35 | 36 | this.threadStats.threadDumpAll = 37 | this.threadStats.threadDumpRunnable + 38 | this.threadStats.threadDumpWaiting + 39 | this.threadStats.threadDumpTimedWaiting + 40 | this.threadStats.threadDumpBlocked; 41 | } 42 | 43 | get threads(): Thread[] | undefined { 44 | return this._threads; 45 | } 46 | 47 | private _threads: Thread[] | undefined; 48 | 49 | constructor(private modalService: NgbModal) {} 50 | 51 | open(): void { 52 | const modalRef = this.modalService.open(MetricsModalThreadsComponent); 53 | modalRef.componentInstance.threads = this.threads; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | import { VERSION } from 'app/app.constants'; 5 | import { AccountService } from 'app/core/auth/account.service'; 6 | import { LoginService } from 'app/login/login.service'; 7 | import { ProfileService } from 'app/layouts/profiles/profile.service'; 8 | 9 | @Component({ 10 | selector: 'jhi-navbar', 11 | templateUrl: './navbar.component.html', 12 | styleUrls: ['./navbar.component.scss'], 13 | }) 14 | export class NavbarComponent implements OnInit { 15 | inProduction?: boolean; 16 | isNavbarCollapsed = true; 17 | openAPIEnabled?: boolean; 18 | version = ''; 19 | 20 | constructor( 21 | private loginService: LoginService, 22 | private accountService: AccountService, 23 | private profileService: ProfileService, 24 | private router: Router 25 | ) { 26 | if (VERSION) { 27 | this.version = VERSION.toLowerCase().startsWith('v') ? VERSION : 'v' + VERSION; 28 | } 29 | } 30 | 31 | ngOnInit(): void { 32 | this.profileService.getProfileInfo().subscribe(profileInfo => { 33 | this.inProduction = profileInfo.inProduction; 34 | this.openAPIEnabled = profileInfo.openAPIEnabled; 35 | }); 36 | } 37 | 38 | collapseNavbar(): void { 39 | this.isNavbarCollapsed = true; 40 | } 41 | 42 | isAuthenticated(): boolean { 43 | return this.accountService.isAuthenticated(); 44 | } 45 | 46 | login(): void { 47 | this.router.navigate(['/login']); 48 | } 49 | 50 | logout(): void { 51 | this.collapseNavbar(); 52 | this.loginService.logout(); 53 | this.router.navigate(['']); 54 | } 55 | 56 | toggleNavbar(): void { 57 | this.isNavbarCollapsed = !this.isNavbarCollapsed; 58 | } 59 | 60 | getImageUrl(): string { 61 | return this.isAuthenticated() ? this.accountService.getImageUrl() : ''; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.html: -------------------------------------------------------------------------------- 1 |

Threads

2 | 3 | Runnable {{ threadStats.threadDumpRunnable }} 4 | 5 | 12 | {{ (threadStats.threadDumpRunnable * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% 13 | 14 | 15 | Timed Waiting ({{ threadStats.threadDumpTimedWaiting }}) 16 | 17 | 24 | {{ (threadStats.threadDumpTimedWaiting * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% 25 | 26 | 27 | Waiting ({{ threadStats.threadDumpWaiting }}) 28 | 29 | 36 | {{ (threadStats.threadDumpWaiting * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% 37 | 38 | 39 | Blocked ({{ threadStats.threadDumpBlocked }}) 40 | 41 | 48 | {{ (threadStats.threadDumpBlocked * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% 49 | 50 | 51 |
Total: {{ threadStats.threadDumpAll }}
52 | 53 | 56 | -------------------------------------------------------------------------------- /server/src/config/application-dev.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Configuration for the "dev" profile. 3 | # 4 | # This configuration overrides the application.yml file. 5 | # 6 | # More information on profiles: https://www.jhipster.tech/profiles/ 7 | # More information on configuration properties: https://www.jhipster.tech/common-application-properties/ 8 | # =================================================================== 9 | 10 | # =================================================================== 11 | # Standard app properties. 12 | # =================================================================== 13 | 14 | eureka: 15 | instance: 16 | prefer-ip-address: true 17 | client: 18 | service-url: 19 | defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/ 20 | 21 | server: 22 | port: 8081 23 | 24 | mail: 25 | host: localhost 26 | port: 25 27 | username: 28 | password: 29 | 30 | # =================================================================== 31 | # JHipster specific properties 32 | # 33 | # Full reference is available at: https://www.jhipster.tech/common-application-properties/ 34 | # =================================================================== 35 | 36 | jhipster: 37 | mail: # specific JHipster mail property, for standard properties see MailProperties 38 | from: gen@localhost 39 | base-url: http://127.0.0.1:8082 40 | # =================================================================== 41 | # Application specific properties 42 | # Add your own application properties here, see the ApplicationProperties class 43 | # to have type-safe configuration, like in the JHipsterProperties above 44 | # 45 | # More documentation is available at: 46 | # https://www.jhipster.tech/common-application-properties/ 47 | # =================================================================== 48 | 49 | # application: 50 | -------------------------------------------------------------------------------- /server/src/config/application-test.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Configuration for the "test" profile. 3 | # 4 | # This configuration overrides the application.yml file. 5 | # 6 | # More information on profiles: https://www.jhipster.tech/profiles/ 7 | # More information on configuration properties: https://www.jhipster.tech/common-application-properties/ 8 | # =================================================================== 9 | 10 | # =================================================================== 11 | # Standard app properties. 12 | # =================================================================== 13 | 14 | eureka: 15 | instance: 16 | prefer-ip-address: true 17 | client: 18 | service-url: 19 | defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/ 20 | 21 | server: 22 | port: 8081 23 | 24 | mail: 25 | host: localhost 26 | port: 25 27 | username: 28 | password: 29 | 30 | # =================================================================== 31 | # JHipster specific properties 32 | # 33 | # Full reference is available at: https://www.jhipster.tech/common-application-properties/ 34 | # =================================================================== 35 | 36 | jhipster: 37 | mail: # specific JHipster mail property, for standard properties see MailProperties 38 | from: gen@localhost 39 | base-url: http://127.0.0.1:8082 40 | # =================================================================== 41 | # Application specific properties 42 | # Add your own application properties here, see the ApplicationProperties class 43 | # to have type-safe configuration, like in the JHipsterProperties above 44 | # 45 | # More documentation is available at: 46 | # https://www.jhipster.tech/common-application-properties/ 47 | # =================================================================== 48 | 49 | # application: 50 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/metrics.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | Application Metrics 4 | 5 | 6 |

7 | 8 |

JVM Metrics

9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 23 | 24 |
Updating...
25 | 26 | 31 | 32 | 37 | 38 | 43 | 44 | 49 |
50 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.html: -------------------------------------------------------------------------------- 1 |

Cache statistics

2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 39 | 40 | 41 |
Cache nameCache HitsCache MissesCache GetsCache PutsCache RemovalsCache EvictionsCache Hit %Cache Miss %
{{ entry.key }}{{ entry.value['cache.gets.hit'] }}{{ entry.value['cache.gets.miss'] }}{{ entry.value['cache.gets.hit'] + entry.value['cache.gets.miss'] }}{{ entry.value['cache.puts'] }}{{ entry.value['cache.removals'] }}{{ entry.value['cache.evictions'] }} 28 | {{ 29 | filterNaN((100 * entry.value['cache.gets.hit']) / (entry.value['cache.gets.hit'] + entry.value['cache.gets.miss'])) 30 | | number: '1.0-4' 31 | }} 32 | 34 | {{ 35 | filterNaN((100 * entry.value['cache.gets.miss']) / (entry.value['cache.gets.hit'] + entry.value['cache.gets.miss'])) 36 | | number: '1.0-4' 37 | }} 38 |
42 |
43 | -------------------------------------------------------------------------------- /server/src/orm.config.ts: -------------------------------------------------------------------------------- 1 | import { TypeOrmModuleOptions } from '@nestjs/typeorm'; 2 | 3 | function ormConfig(): TypeOrmModuleOptions { 4 | const commonConf = { 5 | SYNCRONIZE: false, 6 | ENTITIES: [__dirname + '/domain/*.entity{.ts,.js}'], 7 | MIGRATIONS: [__dirname + '/migrations/**/*{.ts,.js}'], 8 | CLI: { 9 | migrationsDir: 'src/migrations', 10 | }, 11 | MIGRATIONS_RUN: true, 12 | }; 13 | 14 | let ormconfig: TypeOrmModuleOptions = { 15 | name: 'default', 16 | type: 'sqlite', 17 | database: '../target/db/sqlite-dev-db.sql', 18 | logging: true, 19 | synchronize: true, 20 | entities: commonConf.ENTITIES, 21 | migrations: commonConf.MIGRATIONS, 22 | cli: commonConf.CLI, 23 | migrationsRun: commonConf.MIGRATIONS_RUN, 24 | }; 25 | 26 | if (process.env.BACKEND_ENV === 'prod') { 27 | ormconfig = { 28 | name: 'default', 29 | type: 'mysql', 30 | database: 'gen', 31 | host: 'localhost', 32 | port: 3307, 33 | username: 'sa', 34 | password: 'yourStrong(!)Password', 35 | logging: false, 36 | synchronize: commonConf.SYNCRONIZE, 37 | entities: commonConf.ENTITIES, 38 | migrations: commonConf.MIGRATIONS, 39 | cli: commonConf.CLI, 40 | migrationsRun: commonConf.MIGRATIONS_RUN, 41 | }; 42 | } 43 | 44 | if (process.env.BACKEND_ENV === 'test') { 45 | ormconfig = { 46 | name: 'default', 47 | type: 'sqlite', 48 | database: ':memory:', 49 | keepConnectionAlive: true, 50 | logging: true, 51 | synchronize: true, 52 | entities: commonConf.ENTITIES, 53 | migrations: commonConf.MIGRATIONS, 54 | cli: commonConf.CLI, 55 | migrationsRun: commonConf.MIGRATIONS_RUN, 56 | }; 57 | } 58 | return ormconfig; 59 | } 60 | 61 | export { ormConfig }; 62 | -------------------------------------------------------------------------------- /src/main/webapp/app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, OnInit, AfterViewInit, ElementRef } from '@angular/core'; 2 | import { FormBuilder, Validators } from '@angular/forms'; 3 | import { Router } from '@angular/router'; 4 | import { LoginService } from 'app/login/login.service'; 5 | import { AccountService } from 'app/core/auth/account.service'; 6 | 7 | @Component({ 8 | selector: 'jhi-login', 9 | templateUrl: './login.component.html', 10 | }) 11 | export class LoginComponent implements OnInit, AfterViewInit { 12 | @ViewChild('username', { static: false }) 13 | username?: ElementRef; 14 | 15 | authenticationError = false; 16 | 17 | loginForm = this.fb.group({ 18 | username: [null, [Validators.required]], 19 | password: [null, [Validators.required]], 20 | rememberMe: [false], 21 | }); 22 | 23 | constructor( 24 | private accountService: AccountService, 25 | private loginService: LoginService, 26 | private router: Router, 27 | private fb: FormBuilder 28 | ) {} 29 | 30 | ngOnInit(): void { 31 | // if already authenticated then navigate to home page 32 | this.accountService.identity().subscribe(() => { 33 | if (this.accountService.isAuthenticated()) { 34 | this.router.navigate(['']); 35 | } 36 | }); 37 | } 38 | 39 | ngAfterViewInit(): void { 40 | if (this.username) { 41 | this.username.nativeElement.focus(); 42 | } 43 | } 44 | 45 | login(): void { 46 | this.loginService 47 | .login({ 48 | username: this.loginForm.get('username')!.value, 49 | password: this.loginForm.get('password')!.value, 50 | rememberMe: this.loginForm.get('rememberMe')!.value, 51 | }) 52 | .subscribe( 53 | () => { 54 | this.authenticationError = false; 55 | if (!this.router.getCurrentNavigation()) { 56 | // There were no routing during login (eg from navigationToStoredUrl) 57 | this.router.navigate(['']); 58 | } 59 | }, 60 | () => (this.authenticationError = true) 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; 2 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 3 | 4 | import { Thread, ThreadState } from 'app/admin/metrics/metrics.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-thread-modal', 8 | templateUrl: './metrics-modal-threads.component.html', 9 | changeDetection: ChangeDetectionStrategy.OnPush, 10 | }) 11 | export class MetricsModalThreadsComponent implements OnInit { 12 | ThreadState = ThreadState; 13 | threadStateFilter?: ThreadState; 14 | threads?: Thread[]; 15 | threadDumpAll = 0; 16 | threadDumpBlocked = 0; 17 | threadDumpRunnable = 0; 18 | threadDumpTimedWaiting = 0; 19 | threadDumpWaiting = 0; 20 | 21 | constructor(public activeModal: NgbActiveModal) {} 22 | 23 | ngOnInit(): void { 24 | this.threads?.forEach(thread => { 25 | if (thread.threadState === 'RUNNABLE') { 26 | this.threadDumpRunnable += 1; 27 | } else if (thread.threadState === 'WAITING') { 28 | this.threadDumpWaiting += 1; 29 | } else if (thread.threadState === 'TIMED_WAITING') { 30 | this.threadDumpTimedWaiting += 1; 31 | } else if (thread.threadState === 'BLOCKED') { 32 | this.threadDumpBlocked += 1; 33 | } 34 | }); 35 | 36 | this.threadDumpAll = this.threadDumpRunnable + this.threadDumpWaiting + this.threadDumpTimedWaiting + this.threadDumpBlocked; 37 | } 38 | 39 | getBadgeClass(threadState: ThreadState): string { 40 | if (threadState === 'RUNNABLE') { 41 | return 'badge-success'; 42 | } else if (threadState === 'WAITING') { 43 | return 'badge-info'; 44 | } else if (threadState === 'TIMED_WAITING') { 45 | return 'badge-warning'; 46 | } else if (threadState === 'BLOCKED') { 47 | return 'badge-danger'; 48 | } 49 | return ''; 50 | } 51 | 52 | getThreads(): Thread[] { 53 | return this.threads?.filter(thread => !this.threadStateFilter || thread.threadState === this.threadStateFilter) ?? []; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; 2 | import { FormBuilder, Validators } from '@angular/forms'; 3 | import { ActivatedRoute } from '@angular/router'; 4 | 5 | import { PasswordResetFinishService } from './password-reset-finish.service'; 6 | 7 | @Component({ 8 | selector: 'jhi-password-reset-finish', 9 | templateUrl: './password-reset-finish.component.html', 10 | }) 11 | export class PasswordResetFinishComponent implements OnInit, AfterViewInit { 12 | @ViewChild('newPassword', { static: false }) 13 | newPassword?: ElementRef; 14 | 15 | initialized = false; 16 | doNotMatch = false; 17 | error = false; 18 | success = false; 19 | key = ''; 20 | 21 | passwordForm = this.fb.group({ 22 | newPassword: ['', [Validators.required, Validators.minLength(4), Validators.maxLength(50)]], 23 | confirmPassword: ['', [Validators.required, Validators.minLength(4), Validators.maxLength(50)]], 24 | }); 25 | 26 | constructor(private passwordResetFinishService: PasswordResetFinishService, private route: ActivatedRoute, private fb: FormBuilder) {} 27 | 28 | ngOnInit(): void { 29 | this.route.queryParams.subscribe(params => { 30 | if (params['key']) { 31 | this.key = params['key']; 32 | } 33 | this.initialized = true; 34 | }); 35 | } 36 | 37 | ngAfterViewInit(): void { 38 | if (this.newPassword) { 39 | this.newPassword.nativeElement.focus(); 40 | } 41 | } 42 | 43 | finishReset(): void { 44 | this.doNotMatch = false; 45 | this.error = false; 46 | 47 | const newPassword = this.passwordForm.get(['newPassword'])!.value; 48 | const confirmPassword = this.passwordForm.get(['confirmPassword'])!.value; 49 | 50 | if (newPassword !== confirmPassword) { 51 | this.doNotMatch = true; 52 | } else { 53 | this.passwordResetFinishService.save(this.key, newPassword).subscribe( 54 | () => (this.success = true), 55 | () => (this.error = true) 56 | ); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/event-manager.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, Observer, Subscription } from 'rxjs'; 3 | import { filter, share } from 'rxjs/operators'; 4 | 5 | export class EventWithContent { 6 | constructor(public name: string, public content: T) {} 7 | } 8 | 9 | /** 10 | * An utility class to manage RX events 11 | */ 12 | @Injectable({ 13 | providedIn: 'root', 14 | }) 15 | export class EventManager { 16 | observable: Observable | string>; 17 | observer?: Observer | string>; 18 | 19 | constructor() { 20 | this.observable = new Observable((observer: Observer | string>) => { 21 | this.observer = observer; 22 | }).pipe(share()); 23 | } 24 | 25 | /** 26 | * Method to broadcast the event to observer 27 | */ 28 | broadcast(event: EventWithContent | string): void { 29 | if (this.observer) { 30 | this.observer.next(event); 31 | } 32 | } 33 | 34 | /** 35 | * Method to subscribe to an event with callback 36 | * @param eventNames Single event name or array of event names to what subscribe 37 | * @param callback Callback to run when the event occurs 38 | */ 39 | subscribe(eventNames: string | string[], callback: (event: EventWithContent | string) => void): Subscription { 40 | if (typeof eventNames === 'string') { 41 | eventNames = [eventNames]; 42 | } 43 | return this.observable 44 | .pipe( 45 | filter((event: EventWithContent | string) => { 46 | for (const eventName of eventNames) { 47 | if ((typeof event === 'string' && event === eventName) || (typeof event !== 'string' && event.name === eventName)) { 48 | return true; 49 | } 50 | } 51 | return false; 52 | }) 53 | ) 54 | .subscribe(callback); 55 | } 56 | 57 | /** 58 | * Method to unsubscribe the subscription 59 | */ 60 | destroy(subscriber: Subscription): void { 61 | subscriber.unsubscribe(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/auth/auth-jwt.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; 6 | 7 | import { ApplicationConfigService } from '../config/application-config.service'; 8 | import { Login } from 'app/login/login.model'; 9 | 10 | type JwtToken = { 11 | id_token: string; 12 | }; 13 | 14 | @Injectable({ providedIn: 'root' }) 15 | export class AuthServerProvider { 16 | constructor( 17 | private http: HttpClient, 18 | private $localStorage: LocalStorageService, 19 | private $sessionStorage: SessionStorageService, 20 | private applicationConfigService: ApplicationConfigService 21 | ) {} 22 | 23 | getToken(): string { 24 | const tokenInLocalStorage: string | null = this.$localStorage.retrieve('authenticationToken'); 25 | const tokenInSessionStorage: string | null = this.$sessionStorage.retrieve('authenticationToken'); 26 | return tokenInLocalStorage ?? tokenInSessionStorage ?? ''; 27 | } 28 | 29 | login(credentials: Login): Observable { 30 | return this.http 31 | .post(this.applicationConfigService.getEndpointFor('api/authenticate'), credentials) 32 | .pipe(map(response => this.authenticateSuccess(response, credentials.rememberMe))); 33 | } 34 | 35 | logout(): Observable { 36 | return new Observable(observer => { 37 | this.$localStorage.clear('authenticationToken'); 38 | this.$sessionStorage.clear('authenticationToken'); 39 | observer.complete(); 40 | }); 41 | } 42 | 43 | private authenticateSuccess(response: JwtToken, rememberMe: boolean): void { 44 | const jwt = response.id_token; 45 | if (rememberMe) { 46 | this.$localStorage.store('authenticationToken', jwt); 47 | this.$sessionStorage.clear('authenticationToken'); 48 | } else { 49 | this.$sessionStorage.store('authenticationToken', jwt); 50 | this.$localStorage.clear('authenticationToken'); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/detail/user-management-detail.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { of } from 'rxjs'; 4 | 5 | import { Authority } from 'app/config/authority.constants'; 6 | import { User } from '../user-management.model'; 7 | 8 | import { UserManagementDetailComponent } from './user-management-detail.component'; 9 | 10 | describe('Component Tests', () => { 11 | describe('User Management Detail Component', () => { 12 | let comp: UserManagementDetailComponent; 13 | let fixture: ComponentFixture; 14 | 15 | beforeEach( 16 | waitForAsync(() => { 17 | TestBed.configureTestingModule({ 18 | declarations: [UserManagementDetailComponent], 19 | providers: [ 20 | { 21 | provide: ActivatedRoute, 22 | useValue: { 23 | data: of({ user: new User(123, 'user', 'first', 'last', 'first@last.com', true, 'en', [Authority.USER], 'admin') }), 24 | }, 25 | }, 26 | ], 27 | }) 28 | .overrideTemplate(UserManagementDetailComponent, '') 29 | .compileComponents(); 30 | }) 31 | ); 32 | 33 | beforeEach(() => { 34 | fixture = TestBed.createComponent(UserManagementDetailComponent); 35 | comp = fixture.componentInstance; 36 | }); 37 | 38 | describe('OnInit', () => { 39 | it('Should call load all on init', () => { 40 | // GIVEN 41 | 42 | // WHEN 43 | comp.ngOnInit(); 44 | 45 | // THEN 46 | expect(comp.user).toEqual( 47 | jasmine.objectContaining({ 48 | id: 123, 49 | login: 'user', 50 | firstName: 'first', 51 | lastName: 'last', 52 | email: 'first@last.com', 53 | activated: true, 54 | langKey: 'en', 55 | authorities: [Authority.USER], 56 | createdBy: 'admin', 57 | }) 58 | ); 59 | }); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/main/webapp/app/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | jest.mock('app/core/auth/account.service'); 2 | jest.mock('@angular/router'); 3 | 4 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 5 | import { Router } from '@angular/router'; 6 | import { of } from 'rxjs'; 7 | 8 | import { AccountService } from 'app/core/auth/account.service'; 9 | 10 | import { HomeComponent } from './home.component'; 11 | 12 | describe('Component Tests', () => { 13 | describe('Home Component', () => { 14 | let comp: HomeComponent; 15 | let fixture: ComponentFixture; 16 | let mockAccountService: AccountService; 17 | let mockRouter: Router; 18 | 19 | beforeEach( 20 | waitForAsync(() => { 21 | TestBed.configureTestingModule({ 22 | declarations: [HomeComponent], 23 | providers: [AccountService, Router], 24 | }) 25 | .overrideTemplate(HomeComponent, '') 26 | .compileComponents(); 27 | }) 28 | ); 29 | 30 | beforeEach(() => { 31 | fixture = TestBed.createComponent(HomeComponent); 32 | comp = fixture.componentInstance; 33 | mockAccountService = TestBed.inject(AccountService); 34 | mockAccountService.identity = jest.fn(() => of(null)); 35 | mockAccountService.getAuthenticationState = jest.fn(() => of(null)); 36 | mockRouter = TestBed.inject(Router); 37 | }); 38 | 39 | it('Should call accountService.getAuthenticationState on init', () => { 40 | // WHEN 41 | comp.ngOnInit(); 42 | 43 | // THEN 44 | expect(mockAccountService.getAuthenticationState).toHaveBeenCalled(); 45 | }); 46 | 47 | it('Should call accountService.isAuthenticated when it checks authentication', () => { 48 | // WHEN 49 | comp.isAuthenticated(); 50 | 51 | // THEN 52 | expect(mockAccountService.isAuthenticated).toHaveBeenCalled(); 53 | }); 54 | 55 | it('Should navigate to /login on login', () => { 56 | // WHEN 57 | comp.login(); 58 | 59 | // THEN 60 | expect(mockRouter.navigate).toHaveBeenCalledWith(['/login']); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /server/src/web/rest/public.user.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, ClassSerializerInterceptor, Get, Logger, Req, UseInterceptors } from '@nestjs/common'; 2 | import { LoggingInterceptor } from '../../client/interceptors/logging.interceptor'; 3 | import { PageRequest, Page } from '../../domain/base/pagination.entity'; 4 | import { UserDTO } from '../../service/dto/user.dto'; 5 | import { Request } from 'express'; 6 | import { HeaderUtil } from '../../client/header-util'; 7 | import { ApiUseTags, ApiResponse, ApiOperation } from '@nestjs/swagger'; 8 | import { AuthService } from '../../service/auth.service'; 9 | 10 | @Controller('api') 11 | @UseInterceptors(LoggingInterceptor, ClassSerializerInterceptor) 12 | @ApiUseTags('public-user-controller') 13 | export class PublicUserController { 14 | logger = new Logger('PublicUserController'); 15 | 16 | constructor(private readonly authService: AuthService) {} 17 | 18 | @Get('/users') 19 | @ApiOperation({ title: 'Get the list of users' }) 20 | @ApiResponse({ 21 | status: 200, 22 | description: 'List all users records', 23 | type: UserDTO, 24 | }) 25 | async getAllPublicUsers(@Req() req: Request): Promise { 26 | const sortField = req.query.sort; 27 | const pageRequest: PageRequest = new PageRequest(req.query.page, req.query.size, sortField); 28 | const [results, count] = await this.authService.getAllUsers({ 29 | skip: +pageRequest.page * pageRequest.size, 30 | take: +pageRequest.size, 31 | order: pageRequest.sort.asOrder(), 32 | }); 33 | HeaderUtil.addPaginationHeaders(req.res, new Page(results, count, pageRequest)); 34 | return results; 35 | } 36 | 37 | @Get('/authorities') 38 | @ApiOperation({ title: 'Get the list of user roles' }) 39 | @ApiResponse({ 40 | status: 200, 41 | description: 'List all user roles', 42 | type: 'string', 43 | isArray: true, 44 | }) 45 | getAuthorities(@Req() req: any): any { 46 | const user: any = req.user; 47 | if (!user) { 48 | return []; 49 | } 50 | return user.authorities; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | jest.mock('@ng-bootstrap/ng-bootstrap'); 2 | 3 | import { ComponentFixture, TestBed, waitForAsync, inject, fakeAsync, tick } from '@angular/core/testing'; 4 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 5 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 6 | import { of } from 'rxjs'; 7 | 8 | import { UserManagementService } from '../service/user-management.service'; 9 | 10 | import { UserManagementDeleteDialogComponent } from './user-management-delete-dialog.component'; 11 | 12 | describe('Component Tests', () => { 13 | describe('User Management Delete Component', () => { 14 | let comp: UserManagementDeleteDialogComponent; 15 | let fixture: ComponentFixture; 16 | let service: UserManagementService; 17 | let mockActiveModal: NgbActiveModal; 18 | 19 | beforeEach( 20 | waitForAsync(() => { 21 | TestBed.configureTestingModule({ 22 | imports: [HttpClientTestingModule], 23 | declarations: [UserManagementDeleteDialogComponent], 24 | providers: [NgbActiveModal], 25 | }) 26 | .overrideTemplate(UserManagementDeleteDialogComponent, '') 27 | .compileComponents(); 28 | }) 29 | ); 30 | 31 | beforeEach(() => { 32 | fixture = TestBed.createComponent(UserManagementDeleteDialogComponent); 33 | comp = fixture.componentInstance; 34 | service = TestBed.inject(UserManagementService); 35 | mockActiveModal = TestBed.inject(NgbActiveModal); 36 | }); 37 | 38 | describe('confirmDelete', () => { 39 | it('Should call delete service on confirmDelete', inject( 40 | [], 41 | fakeAsync(() => { 42 | // GIVEN 43 | spyOn(service, 'delete').and.returnValue(of({})); 44 | 45 | // WHEN 46 | comp.confirmDelete('user'); 47 | tick(); 48 | 49 | // THEN 50 | expect(service.delete).toHaveBeenCalledWith('user'); 51 | expect(mockActiveModal.close).toHaveBeenCalledWith('deleted'); 52 | }) 53 | )); 54 | }); 55 | }); 56 | }); 57 | --------------------------------------------------------------------------------