@mshima/generator-jhipster-nodejs 0.0.0-stage → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE.txt +204 -0
  2. package/README.md +243 -2
  3. package/build-info.json +3 -0
  4. package/cli/cli-customizations.cjs +9 -0
  5. package/cli/cli.cjs +40 -0
  6. package/cli/logo.js +31 -0
  7. package/cli/version.cjs +12 -0
  8. package/generators/ci-cd/USAGE +5 -0
  9. package/generators/ci-cd/command.js +5 -0
  10. package/generators/ci-cd/generator.js +158 -0
  11. package/generators/ci-cd/index.js +2 -0
  12. package/generators/ci-cd/templates/.gitlab-ci.yml.ejs +137 -0
  13. package/generators/ci-cd/templates/azure-pipelines.yml.ejs +68 -0
  14. package/generators/ci-cd/templates/circle.yml.ejs +59 -0
  15. package/generators/ci-cd/templates/docker-registry.yml.ejs +58 -0
  16. package/generators/ci-cd/templates/github-ci.yml.ejs +90 -0
  17. package/generators/ci-cd/templates/jenkins/Jenkinsfile.ejs +104 -0
  18. package/generators/ci-cd/templates/jenkins/idea.gdsl +90 -0
  19. package/generators/ci-cd/templates/jenkins/jenkins.yml.ejs +33 -0
  20. package/generators/ci-cd/templates/travis.yml.ejs +75 -0
  21. package/generators/client/command.js +5 -0
  22. package/generators/client/generator.js +21 -0
  23. package/generators/client/generators/bootstrap/command.js +5 -0
  24. package/generators/client/generators/bootstrap/generator.js +28 -0
  25. package/generators/client/generators/bootstrap/index.js +2 -0
  26. package/generators/client/index.js +2 -0
  27. package/generators/common/generators/bootstrap/command.js +5 -0
  28. package/generators/common/generators/bootstrap/generator.js +37 -0
  29. package/generators/common/generators/bootstrap/index.js +2 -0
  30. package/generators/docker/command.js +5 -0
  31. package/generators/docker/generator.js +16 -0
  32. package/generators/docker/generators/bootstrap/command.js +5 -0
  33. package/generators/docker/generators/bootstrap/generator.js +56 -0
  34. package/generators/docker/generators/bootstrap/index.js +2 -0
  35. package/generators/docker/index.js +2 -0
  36. package/generators/docker/templates/docker/app.yml.ejs +25 -0
  37. package/generators/generator-nodejs-constants.js +32 -0
  38. package/generators/languages/command.js +5 -0
  39. package/generators/languages/generator.js +22 -0
  40. package/generators/languages/index.js +2 -0
  41. package/generators/languages/templates/src/main/webapp/i18n/en/home.json +19 -0
  42. package/generators/languages/templates/src/main/webapp/i18n/es/home.json +19 -0
  43. package/generators/languages/templates/src/main/webapp/i18n/it/home.json +19 -0
  44. package/generators/node-server/command.js +96 -0
  45. package/generators/node-server/entity-files.js +26 -0
  46. package/generators/node-server/files.js +254 -0
  47. package/generators/node-server/generator.js +705 -0
  48. package/generators/node-server/generators/bootstrap/command.js +5 -0
  49. package/generators/node-server/generators/bootstrap/generator.js +84 -0
  50. package/generators/node-server/generators/bootstrap/index.js +2 -0
  51. package/generators/node-server/generators/migrations/command.js +5 -0
  52. package/generators/node-server/generators/migrations/files.js +42 -0
  53. package/generators/node-server/generators/migrations/generator.js +108 -0
  54. package/generators/node-server/generators/migrations/index.js +2 -0
  55. package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityConstraintsMigration_.ts.ejs +40 -0
  56. package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityMigration_.ts.ejs +26 -0
  57. package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityRemovedMigration_.ts.ejs +15 -0
  58. package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityUpdatedConstraintsMigration_.ts.ejs +60 -0
  59. package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityUpdatedMigration_.ts.ejs +28 -0
  60. package/generators/node-server/index.js +2 -0
  61. package/generators/node-server/resources/package.json +76 -0
  62. package/generators/node-server/support/api-fake-data.js +54 -0
  63. package/generators/node-server/support/columns.js +73 -0
  64. package/generators/node-server/support/database.js +24 -0
  65. package/generators/node-server/support/entity-authorities.js +32 -0
  66. package/generators/node-server/support/entity-paths.js +13 -0
  67. package/generators/node-server/support/enums.js +11 -0
  68. package/generators/node-server/support/index.js +9 -0
  69. package/generators/node-server/support/mail-templates.js +82 -0
  70. package/generators/node-server/support/migrations.js +376 -0
  71. package/generators/node-server/support/validators.js +25 -0
  72. package/generators/node-server/templates/.dockerignore.ejs +16 -0
  73. package/generators/node-server/templates/.gitignore.jhi.nodejs.ejs +24 -0
  74. package/generators/node-server/templates/Dockerfile.ejs +54 -0
  75. package/generators/node-server/templates/README.md.jhi.nodejs.ejs +220 -0
  76. package/generators/node-server/templates/package.json.ejs +82 -0
  77. package/generators/node-server/templates/server/README.md.ejs +277 -0
  78. package/generators/node-server/templates/server/e2e/_entityFileName_.database.e2e-spec.ts.ejs +192 -0
  79. package/generators/node-server/templates/server/e2e/_entityFileName_.e2e-spec.ts.ejs +402 -0
  80. package/generators/node-server/templates/server/e2e/account.e2e-spec.ts.ejs +349 -0
  81. package/generators/node-server/templates/server/e2e/app.e2e-spec.ts.ejs +188 -0
  82. package/generators/node-server/templates/server/e2e/authority.e2e-spec.ts.ejs +50 -0
  83. package/generators/node-server/templates/server/e2e/cors.e2e-spec.ts.ejs +110 -0
  84. package/generators/node-server/templates/server/e2e/management.e2e-spec.ts.ejs +180 -0
  85. package/generators/node-server/templates/server/e2e/oauth2-bearer.e2e-spec.ts.ejs +71 -0
  86. package/generators/node-server/templates/server/e2e/session.e2e-spec.ts.ejs +234 -0
  87. package/generators/node-server/templates/server/e2e/setup.test.js.ejs +1 -0
  88. package/generators/node-server/templates/server/e2e/user.e2e-spec.ts.ejs +318 -0
  89. package/generators/node-server/templates/server/env.ejs +1 -0
  90. package/generators/node-server/templates/server/eslint.config.mjs.ejs +48 -0
  91. package/generators/node-server/templates/server/nest-cli.json.ejs +11 -0
  92. package/generators/node-server/templates/server/ormconfig.ts.ejs +35 -0
  93. package/generators/node-server/templates/server/package.json.ejs +143 -0
  94. package/generators/node-server/templates/server/sonar-project.properties.ejs +10 -0
  95. package/generators/node-server/templates/server/src/app.config.spec.ts.ejs +13 -0
  96. package/generators/node-server/templates/server/src/app.config.ts.ejs +179 -0
  97. package/generators/node-server/templates/server/src/app.module.ts.ejs +80 -0
  98. package/generators/node-server/templates/server/src/application-config.ts.ejs +153 -0
  99. package/generators/node-server/templates/server/src/auth/account.controller.ts.ejs +183 -0
  100. package/generators/node-server/templates/server/src/auth/auth.module.ts.ejs +104 -0
  101. package/generators/node-server/templates/server/src/auth/auth.service.ts.ejs +294 -0
  102. package/generators/node-server/templates/server/src/auth/csrf.middleware.spec.ts.ejs +76 -0
  103. package/generators/node-server/templates/server/src/auth/csrf.middleware.ts.ejs +85 -0
  104. package/generators/node-server/templates/server/src/auth/decorators/auth-user.decorator.ts.ejs +8 -0
  105. package/generators/node-server/templates/server/src/auth/decorators/roles.decorator.ts.ejs +4 -0
  106. package/generators/node-server/templates/server/src/auth/dto/key-and-password.dto.ts.ejs +17 -0
  107. package/generators/node-server/templates/server/src/auth/dto/password-change.dto.ts.ejs +17 -0
  108. package/generators/node-server/templates/server/src/auth/dto/user-login.dto.ts.ejs +21 -0
  109. package/generators/node-server/templates/server/src/auth/guards/auth.guard.ts.ejs +50 -0
  110. package/generators/node-server/templates/server/src/auth/guards/oauth2-login.guard.ts.ejs +6 -0
  111. package/generators/node-server/templates/server/src/auth/guards/roles.guard.ts.ejs +25 -0
  112. package/generators/node-server/templates/server/src/auth/index.ts.ejs +8 -0
  113. package/generators/node-server/templates/server/src/auth/jwt.config.spec.ts.ejs +22 -0
  114. package/generators/node-server/templates/server/src/auth/jwt.config.ts.ejs +29 -0
  115. package/generators/node-server/templates/server/src/auth/oauth2.authorities.spec.ts.ejs +24 -0
  116. package/generators/node-server/templates/server/src/auth/oauth2.authorities.ts.ejs +33 -0
  117. package/generators/node-server/templates/server/src/auth/oauth2.config.ts.ejs +51 -0
  118. package/generators/node-server/templates/server/src/auth/passport.jwt.strategy.ts.ejs +45 -0
  119. package/generators/node-server/templates/server/src/auth/passport.oauth2-bearer.strategy.ts.ejs +32 -0
  120. package/generators/node-server/templates/server/src/auth/passport.oauth2.strategy.spec.ts.ejs +27 -0
  121. package/generators/node-server/templates/server/src/auth/passport.oauth2.strategy.ts.ejs +44 -0
  122. package/generators/node-server/templates/server/src/auth/password-util.ts.ejs +10 -0
  123. package/generators/node-server/templates/server/src/auth/password.service.ts.ejs +34 -0
  124. package/generators/node-server/templates/server/src/auth/payload.interface.ts.ejs +14 -0
  125. package/generators/node-server/templates/server/src/auth/role-type.ts.ejs +7 -0
  126. package/generators/node-server/templates/server/src/auth/session-user.middleware.ts.ejs +33 -0
  127. package/generators/node-server/templates/server/src/auth/session.entity.ts.ejs +36 -0
  128. package/generators/node-server/templates/server/src/auth/session.store.spec.ts.ejs +99 -0
  129. package/generators/node-server/templates/server/src/auth/session.store.ts.ejs +116 -0
  130. package/generators/node-server/templates/server/src/auth/user.jwt.controller.ts.ejs +26 -0
  131. package/generators/node-server/templates/server/src/auth/user.oauth2.controller.ts.ejs +78 -0
  132. package/generators/node-server/templates/server/src/auth/user.session.controller.ts.ejs +103 -0
  133. package/generators/node-server/templates/server/src/common/common.module.ts.ejs +19 -0
  134. package/generators/node-server/templates/server/src/common/database/base.entity.ts.ejs +13 -0
  135. package/generators/node-server/templates/server/src/common/database/column-transformers.spec.ts.ejs +46 -0
  136. package/generators/node-server/templates/server/src/common/database/column-transformers.ts.ejs +69 -0
  137. package/generators/node-server/templates/server/src/common/database/column-types.spec.ts.ejs +21 -0
  138. package/generators/node-server/templates/server/src/common/database/column-types.ts.ejs +29 -0
  139. package/generators/node-server/templates/server/src/common/database/pagination.entity.spec.ts.ejs +66 -0
  140. package/generators/node-server/templates/server/src/common/database/pagination.entity.ts.ejs +108 -0
  141. package/generators/node-server/templates/server/src/common/dto/base.dto.ts.ejs +19 -0
  142. package/generators/node-server/templates/server/src/common/errors/error-constants.ts.ejs +10 -0
  143. package/generators/node-server/templates/server/src/common/errors/problem-details.filter.ts.ejs +63 -0
  144. package/generators/node-server/templates/server/src/common/errors/problem.exception.ts.ejs +92 -0
  145. package/generators/node-server/templates/server/src/common/field_validators.ejs +77 -0
  146. package/generators/node-server/templates/server/src/common/header-util.ts.ejs +105 -0
  147. package/generators/node-server/templates/server/src/common/interceptors/logging.interceptor.spec.ts.ejs +93 -0
  148. package/generators/node-server/templates/server/src/common/interceptors/logging.interceptor.ts.ejs +48 -0
  149. package/generators/node-server/templates/server/src/common/logging.spec.ts.ejs +83 -0
  150. package/generators/node-server/templates/server/src/common/logging.ts.ejs +129 -0
  151. package/generators/node-server/templates/server/src/common/metrics.spec.ts.ejs +43 -0
  152. package/generators/node-server/templates/server/src/common/metrics.ts.ejs +198 -0
  153. package/generators/node-server/templates/server/src/common/request.ts.ejs +8 -0
  154. package/generators/node-server/templates/server/src/common/sortable-properties.ts.ejs +43 -0
  155. package/generators/node-server/templates/server/src/common/updatable-properties.ts.ejs +14 -0
  156. package/generators/node-server/templates/server/src/config/application-dev.yml.ejs +118 -0
  157. package/generators/node-server/templates/server/src/config/application-prod.yml.ejs +97 -0
  158. package/generators/node-server/templates/server/src/config/application-secret-samples.yml.ejs +42 -0
  159. package/generators/node-server/templates/server/src/config/application-test.yml.ejs +68 -0
  160. package/generators/node-server/templates/server/src/config/application-tls-samples.yml.ejs +15 -0
  161. package/generators/node-server/templates/server/src/config/application-tls.yml.ejs +15 -0
  162. package/generators/node-server/templates/server/src/config/application.yml.ejs +149 -0
  163. package/generators/node-server/templates/server/src/config.spec.ts.ejs +81 -0
  164. package/generators/node-server/templates/server/src/config.ts.ejs +255 -0
  165. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.controller.ts.ejs +178 -0
  166. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.entity.ts.ejs +110 -0
  167. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.mapper.ts.ejs +37 -0
  168. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.module.ts.ejs +14 -0
  169. package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.service.ts.ejs +123 -0
  170. package/generators/node-server/templates/server/src/entities/_entityFileName_/dto/_entityFileName_.dto.ts.ejs +99 -0
  171. package/generators/node-server/templates/server/src/mail/mail.module.ts.ejs +49 -0
  172. package/generators/node-server/templates/server/src/mail/mail.service.spec.ts.ejs +105 -0
  173. package/generators/node-server/templates/server/src/mail/mail.service.ts.ejs +120 -0
  174. package/generators/node-server/templates/server/src/main.ts.ejs +114 -0
  175. package/generators/node-server/templates/server/src/management/api-docs.controller.ts.ejs +27 -0
  176. package/generators/node-server/templates/server/src/management/api-docs.service.ts.ejs +8 -0
  177. package/generators/node-server/templates/server/src/management/configuration.controller.ts.ejs +80 -0
  178. package/generators/node-server/templates/server/src/management/health.controller.spec.ts.ejs +79 -0
  179. package/generators/node-server/templates/server/src/management/health.controller.ts.ejs +87 -0
  180. package/generators/node-server/templates/server/src/management/health.module.ts.ejs +9 -0
  181. package/generators/node-server/templates/server/src/management/loggers.controller.ts.ejs +40 -0
  182. package/generators/node-server/templates/server/src/management/management.controller.spec.ts.ejs +26 -0
  183. package/generators/node-server/templates/server/src/management/management.controller.ts.ejs +34 -0
  184. package/generators/node-server/templates/server/src/management/management.module.ts.ejs +21 -0
  185. package/generators/node-server/templates/server/src/management/metrics.controller.ts.ejs +85 -0
  186. package/generators/node-server/templates/server/src/migrations/1570200270081-CreateTables.ts.ejs +64 -0
  187. package/generators/node-server/templates/server/src/migrations/1570200270082-CreateSessions.ts.ejs +26 -0
  188. package/generators/node-server/templates/server/src/migrations/1570200490072-SeedUsersRoles.ts.ejs +74 -0
  189. package/generators/node-server/templates/server/src/migrations/support/schema.ts.ejs +254 -0
  190. package/generators/node-server/templates/server/src/orm.config.ts.ejs +147 -0
  191. package/generators/node-server/templates/server/src/registry/consul.service.ts.ejs +70 -0
  192. package/generators/node-server/templates/server/src/registry/eureka.service.ts.ejs +129 -0
  193. package/generators/node-server/templates/server/src/swagger.ts.ejs +24 -0
  194. package/generators/node-server/templates/server/src/tls.ts.ejs +27 -0
  195. package/generators/node-server/templates/server/src/users/authority.controller.ts.ejs +85 -0
  196. package/generators/node-server/templates/server/src/users/authority.entity.ts.ejs +22 -0
  197. package/generators/node-server/templates/server/src/users/dto/authority.dto.ts.ejs +10 -0
  198. package/generators/node-server/templates/server/src/users/dto/user.dto.ts.ejs +116 -0
  199. package/generators/node-server/templates/server/src/users/public.user.controller.ts.ejs +51 -0
  200. package/generators/node-server/templates/server/src/users/user.controller.ts.ejs +191 -0
  201. package/generators/node-server/templates/server/src/users/user.entity.ts.ejs +63 -0
  202. package/generators/node-server/templates/server/src/users/user.mapper.ts.ejs +26 -0
  203. package/generators/node-server/templates/server/src/users/user.module.ts.ejs +19 -0
  204. package/generators/node-server/templates/server/src/users/user.service.ts.ejs +156 -0
  205. package/generators/node-server/templates/server/tsconfig.build.json.ejs +9 -0
  206. package/generators/node-server/templates/server/tsconfig.json.ejs +23 -0
  207. package/generators/node-server/templates/server/typeorm-migration.ts.ejs +18 -0
  208. package/generators/node-server/templates/server/vitest.config.e2e.ts.ejs +16 -0
  209. package/generators/node-server/templates/server/vitest.config.ts.ejs +16 -0
  210. package/generators/server/command.js +7 -0
  211. package/generators/server/generator.js +20 -0
  212. package/generators/server/generators/bootstrap/command.js +5 -0
  213. package/generators/server/generators/bootstrap/generator.js +15 -0
  214. package/generators/server/generators/bootstrap/index.js +2 -0
  215. package/generators/server/index.js +2 -0
  216. package/package.json +75 -4
@@ -0,0 +1,80 @@
1
+ import { Controller, Get, UseGuards } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import { ApiExcludeController } from '@nestjs/swagger';
4
+
5
+ import { activeProfiles, config, configFiles } from '../config.ts';
6
+ import { AuthGuard, Roles, RolesGuard, RoleType } from '../auth/index.ts';
7
+
8
+ const MASK = '******';
9
+
10
+ export interface PropertySource {
11
+ name: string;
12
+ properties: Record<string, { value: unknown }>;
13
+ }
14
+
15
+ export interface Env {
16
+ activeProfiles: string[];
17
+ propertySources: PropertySource[];
18
+ }
19
+
20
+ export interface ConfigProps {
21
+ contexts: Record<string, { beans: Record<string, { prefix: string; properties: Record<string, unknown> }> }>;
22
+ }
23
+
24
+ /**
25
+ * The env and configprops endpoints of Spring Boot Actuator the configuration page of the JHipster clients reads, for
26
+ * the administrators: the sources of the configuration (the environment variables, then the files of the active
27
+ * profiles and application.yml, the highest precedence first) and the properties of the application by prefix. Their
28
+ * values are shown as ****** unless management.endpoint.<endpoint>.show-values is always, like Spring Boot.
29
+ */
30
+ @ApiExcludeController()
31
+ @Controller('management')
32
+ @UseGuards(AuthGuard, RolesGuard)
33
+ @Roles(RoleType.ADMIN)
34
+ export class ConfigurationController {
35
+ constructor(private readonly configService: ConfigService) {}
36
+
37
+ @Get('/env')
38
+ env(): Env {
39
+ const value = this.valueFor('env');
40
+ const source = (name: string, properties: Record<string, unknown>): PropertySource => ({
41
+ name,
42
+ properties: Object.fromEntries(Object.entries(properties).map(([key, property]) => [key, { value: value(property) }])),
43
+ });
44
+ return {
45
+ activeProfiles,
46
+ propertySources: [source('systemEnvironment', process.env), ...configFiles.map(({ name, properties }) => source(name, properties))],
47
+ };
48
+ }
49
+
50
+ @Get('/configprops')
51
+ configprops(): ConfigProps {
52
+ const value = this.valueFor('configprops');
53
+ const beans: ConfigProps['contexts'][string]['beans'] = {};
54
+ for (const [key, property] of Object.entries(config)) {
55
+ const separator = key.indexOf('.');
56
+ if (separator > 0 && typeof property !== 'function') {
57
+ const prefix = key.substring(0, separator);
58
+ beans[prefix] ??= { prefix, properties: {} };
59
+ setNested(beans[prefix].properties, key.substring(separator + 1).split('.'), value(property));
60
+ }
61
+ }
62
+ return { contexts: { [this.configService.get<string>('jhipster.clientApp.name') ?? 'application']: { beans } } };
63
+ }
64
+
65
+ private valueFor(endpoint: 'env' | 'configprops'): (property: unknown) => unknown {
66
+ const showValues = this.configService.get<string>(`management.endpoint.${endpoint}.show-values`) === 'always';
67
+ return property => (showValues ? property : MASK);
68
+ }
69
+ }
70
+
71
+ const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;
72
+
73
+ function setNested(target: Record<string, unknown>, names: string[], value: unknown): void {
74
+ let parent = target;
75
+ for (const name of names.slice(0, -1)) {
76
+ const child = parent[name];
77
+ parent = parent[name] = isRecord(child) ? child : {};
78
+ }
79
+ parent[names[names.length - 1]] = value;
80
+ }
@@ -0,0 +1,79 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { ServiceUnavailableException } from '@nestjs/common';
3
+ import { Reflector } from '@nestjs/core';
4
+ import { Test, TestingModule } from '@nestjs/testing';
5
+ import { HealthCheckService, TypeOrmHealthIndicator } from '@nestjs/terminus';
6
+ import { SKIP_LOGGING } from '../common/interceptors/logging.interceptor.ts';
7
+ import { HealthController } from './health.controller.ts';
8
+
9
+ describe('Health Controller', () => {
10
+ let controller: HealthController;
11
+ const check = vi.fn<HealthCheckService['check']>();
12
+
13
+ beforeEach(async () => {
14
+ check.mockReset();
15
+ const module: TestingModule = await Test.createTestingModule({
16
+ controllers: [HealthController],
17
+ providers: [
18
+ { provide: HealthCheckService, useValue: { check } },
19
+ { provide: TypeOrmHealthIndicator, useValue: { pingCheck: vi.fn() } },
20
+ ],
21
+ }).compile();
22
+
23
+ controller = module.get<HealthController>(HealthController);
24
+ });
25
+
26
+ it('is UP when the database is up', async () => {
27
+ check.mockResolvedValue({ status: 'ok', info: { db: { status: 'up' } }, error: {}, details: { db: { status: 'up' } } });
28
+ const response = { status: vi.fn() };
29
+ await expect(controller.health(response as any)).resolves.toEqual({ status: 'UP', components: { db: { status: 'UP' } } });
30
+ expect(response.status).not.toHaveBeenCalled();
31
+ });
32
+
33
+ it('is DOWN with the status 503 when the database is down', async () => {
34
+ check.mockRejectedValue(
35
+ new ServiceUnavailableException({
36
+ status: 'error',
37
+ info: {},
38
+ error: { db: { status: 'down', message: 'connection refused' } },
39
+ details: { db: { status: 'down', message: 'connection refused' } },
40
+ }),
41
+ );
42
+ const response = { status: vi.fn() };
43
+ // The health of Spring Boot Actuator, not a problem detail: the health checks read it.
44
+ await expect(controller.health(response as any)).resolves.toEqual({ status: 'DOWN', components: { db: { status: 'DOWN' } } });
45
+ expect(response.status).toHaveBeenCalledWith(503);
46
+ });
47
+
48
+ it('the health checks are not logged', () => {
49
+ for (const name of ['health', 'liveness', 'readiness']) {
50
+ const method = Object.getOwnPropertyDescriptor(HealthController.prototype, name)?.value as () => unknown;
51
+ expect(new Reflector().get<boolean>(SKIP_LOGGING, method)).toBe(true);
52
+ }
53
+ });
54
+
55
+ <%_ if (databaseTypeSql) { _%>
56
+ it('answers the liveness probe', () => {
57
+ expect(controller.liveness()).toEqual({ status: 'UP' });
58
+ });
59
+
60
+ it('is ready when the database is up, like the readiness group of JHipster Spring Boot', async () => {
61
+ check.mockResolvedValue({ status: 'ok', info: { db: { status: 'up' } }, error: {}, details: { db: { status: 'up' } } });
62
+ await expect(controller.readiness({ status: vi.fn() } as any)).resolves.toEqual({ status: 'UP' });
63
+ });
64
+
65
+ it('is not ready, with the status 503, when the database is down', async () => {
66
+ check.mockRejectedValue(
67
+ new ServiceUnavailableException({ status: 'error', info: {}, error: { db: { status: 'down' } }, details: { db: { status: 'down' } } }),
68
+ );
69
+ const response = { status: vi.fn() };
70
+ await expect(controller.readiness(response as any)).resolves.toEqual({ status: 'DOWN' });
71
+ expect(response.status).toHaveBeenCalledWith(503);
72
+ });
73
+ <%_ } else { _%>
74
+ it('answers the liveness and readiness probes', () => {
75
+ expect(controller.liveness()).toEqual({ status: 'UP' });
76
+ expect(controller.readiness()).toEqual({ status: 'UP' });
77
+ });
78
+ <%_ } _%>
79
+ });
@@ -0,0 +1,87 @@
1
+ import { Controller, Get, HttpStatus, Res, ServiceUnavailableException } from '@nestjs/common';
2
+ import { ApiExcludeEndpoint } from '@nestjs/swagger';
3
+ import { HealthCheckResult, HealthCheckService, TypeOrmHealthIndicator } from '@nestjs/terminus';
4
+ import { Response } from 'express';
5
+
6
+ import { SkipLogging } from '../common/interceptors/logging.interceptor.ts';
7
+
8
+ export type HealthStatus = 'UP' | 'DOWN';
9
+
10
+ export interface Health {
11
+ status: HealthStatus;
12
+ components: Record<string, { status: HealthStatus }>;
13
+ }
14
+
15
+ /**
16
+ * The health of the application in the format of Spring Boot Actuator: UP when every component (the database) is up,
17
+ * DOWN with the status 503 otherwise. Public and not logged, the container and service discovery health checks call it
18
+ * every few seconds.
19
+ */
20
+ @Controller('management')
21
+ export class HealthController {
22
+ constructor(
23
+ private readonly healthCheckService: HealthCheckService,
24
+ private readonly typeOrmHealthIndicator: TypeOrmHealthIndicator,
25
+ ) {}
26
+
27
+ @ApiExcludeEndpoint()
28
+ @Get('/health')
29
+ @SkipLogging()
30
+ async health(@Res({ passthrough: true }) response: Response): Promise<Health> {
31
+ try {
32
+ return toHealth(await this.healthCheckService.check([() => this.typeOrmHealthIndicator.pingCheck('db')]));
33
+ } catch (error) {
34
+ if (error instanceof ServiceUnavailableException) {
35
+ // The health of Spring Boot Actuator with the status 503, not a problem detail: the health checks read it.
36
+ response.status(HttpStatus.SERVICE_UNAVAILABLE);
37
+ return toHealth(error.getResponse() as HealthCheckResult);
38
+ }
39
+ throw error;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * The liveness probe, like the liveness group of Spring Boot Actuator (livenessState): UP while the application answers.
45
+ */
46
+ @ApiExcludeEndpoint()
47
+ @Get('/health/liveness')
48
+ @SkipLogging()
49
+ liveness(): Pick<Health, 'status'> {
50
+ return { status: 'UP' };
51
+ }
52
+
53
+ <%_ if (databaseTypeSql) { _%>
54
+ /**
55
+ * The readiness probe, like the readiness group of JHipster Spring Boot (readinessState,db): UP once the application
56
+ * accepts requests and reaches its database, DOWN with the status 503 otherwise. A JHipster gateway waits for it before
57
+ * routing to a microservice.
58
+ */
59
+ @ApiExcludeEndpoint()
60
+ @Get('/health/readiness')
61
+ @SkipLogging()
62
+ async readiness(@Res({ passthrough: true }) response: Response): Promise<Pick<Health, 'status'>> {
63
+ const { status } = await this.health(response);
64
+ return { status };
65
+ }
66
+ <%_ } else { _%>
67
+ /**
68
+ * The readiness probe, like the readiness group of Spring Boot Actuator (readinessState): UP once the application
69
+ * accepts requests. A JHipster gateway waits for it before routing to a microservice.
70
+ */
71
+ @ApiExcludeEndpoint()
72
+ @Get('/health/readiness')
73
+ @SkipLogging()
74
+ readiness(): Pick<Health, 'status'> {
75
+ return { status: 'UP' };
76
+ }
77
+ <%_ } _%>
78
+ }
79
+
80
+ function toHealth({ status, details }: HealthCheckResult): Health {
81
+ return {
82
+ status: status === 'ok' ? 'UP' : 'DOWN',
83
+ components: Object.fromEntries(
84
+ Object.entries(details).map(([name, { status }]) => [name, { status: status === 'up' ? 'UP' : 'DOWN' }]),
85
+ ),
86
+ };
87
+ }
@@ -0,0 +1,9 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { TerminusModule } from '@nestjs/terminus';
3
+ import { HealthController } from './health.controller.ts';
4
+
5
+ @Module({
6
+ imports: [TerminusModule],
7
+ controllers: [HealthController],
8
+ })
9
+ export class HealthModule {}
@@ -0,0 +1,40 @@
1
+ import { BadRequestException, Body, Controller, Get, HttpCode, Param, Post, UseGuards } from '@nestjs/common';
2
+ import { ApiExcludeController } from '@nestjs/swagger';
3
+
4
+ import { LEVELS, Level, LoggerLevel, isLevel, loggerLevels } from '../common/logging.ts';
5
+ import { AuthGuard, Roles, RolesGuard, RoleType } from '../auth/index.ts';
6
+
7
+ export interface Loggers {
8
+ levels: readonly Level[];
9
+ loggers: Record<string, LoggerLevel>;
10
+ }
11
+
12
+ /**
13
+ * The loggers endpoint of Spring Boot Actuator, the logs page of the JHipster clients reads and changes: the level of
14
+ * each logger, changed while the application runs. For the administrators, like the management endpoints of JHipster.
15
+ */
16
+ @ApiExcludeController()
17
+ @Controller('management/loggers')
18
+ @UseGuards(AuthGuard, RolesGuard)
19
+ @Roles(RoleType.ADMIN)
20
+ export class LoggersController {
21
+ @Get()
22
+ loggers(): Loggers {
23
+ return { levels: LEVELS, loggers: loggerLevels.getLevels() };
24
+ }
25
+
26
+ @Get('/:name')
27
+ logger(@Param('name') name: string): LoggerLevel {
28
+ return loggerLevels.getLevel(name);
29
+ }
30
+
31
+ /** Sets the level of a logger, a null `configuredLevel` gives it the level of its parent again. */
32
+ @Post('/:name')
33
+ @HttpCode(204)
34
+ setLevel(@Param('name') name: string, @Body() { configuredLevel }: { configuredLevel?: string | null }): void {
35
+ if (configuredLevel !== null && configuredLevel !== undefined && !isLevel(configuredLevel)) {
36
+ throw new BadRequestException(`Unknown level ${configuredLevel}, expected one of ${LEVELS.join(', ')}`);
37
+ }
38
+ loggerLevels.setLevel(name, configuredLevel ? (configuredLevel.toUpperCase() as Level) : null);
39
+ }
40
+ }
@@ -0,0 +1,26 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import { Test, TestingModule } from '@nestjs/testing';
4
+ import { activeProfiles } from '../config.ts';
5
+ import { ManagementController } from './management.controller.ts';
6
+
7
+ describe('Management Controller', () => {
8
+ let controller: ManagementController;
9
+
10
+ beforeEach(async () => {
11
+ const module: TestingModule = await Test.createTestingModule({
12
+ controllers: [ManagementController],
13
+ providers: [{ provide: ConfigService, useValue: new ConfigService({ info: { 'display-ribbon-on-profiles': 'dev,staging' } }) }],
14
+ }).compile();
15
+
16
+ controller = module.get<ManagementController>(ManagementController);
17
+ });
18
+
19
+ it('should be defined', () => {
20
+ expect(controller).toBeDefined();
21
+ });
22
+
23
+ it('info gives the active profiles and the profiles of the ribbon', () => {
24
+ expect(controller.info()).toEqual({ activeProfiles, 'display-ribbon-on-profiles': 'dev,staging' });
25
+ });
26
+ });
@@ -0,0 +1,34 @@
1
+ import { Controller, Get, Logger } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import { ApiTags, ApiResponse, ApiOperation, ApiExcludeEndpoint } from '@nestjs/swagger';
4
+ import { activeProfiles } from '../config.ts';
5
+
6
+ /** The info of the info endpoint of Spring Boot Actuator the JHipster clients read: the profiles and the ribbon. */
7
+ export interface Info {
8
+ activeProfiles: string[];
9
+ 'display-ribbon-on-profiles': string;
10
+ }
11
+
12
+ @Controller('management')
13
+ @ApiTags('management-controller')
14
+ export class ManagementController {
15
+
16
+ logger = new Logger('ManagementController');
17
+
18
+ constructor(private readonly configService: ConfigService) {}
19
+
20
+ @ApiExcludeEndpoint()
21
+ @Get('/info')
22
+ @ApiOperation({ summary: 'Microservice Info' })
23
+ @ApiResponse({
24
+ status: 200,
25
+ description: 'Check if the microservice is up',
26
+ })
27
+ info(): Info {
28
+ return {
29
+ activeProfiles,
30
+ 'display-ribbon-on-profiles': this.configService.getOrThrow<string>('info.display-ribbon-on-profiles'),
31
+ };
32
+ }
33
+
34
+ }
@@ -0,0 +1,21 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { APP_INTERCEPTOR } from '@nestjs/core';
3
+
4
+ import { MetricsInterceptor, MetricsService } from '../common/metrics.ts';
5
+ import { ApiDocsService } from './api-docs.service.ts';
6
+ import { ApiDocsController } from './api-docs.controller.ts';
7
+ import { ConfigurationController } from './configuration.controller.ts';
8
+ import { LoggersController } from './loggers.controller.ts';
9
+ import { MetricsController } from './metrics.controller.ts';
10
+
11
+ /** The management endpoints of Spring Boot Actuator and the API documentation the admin pages of the JHipster clients use. */
12
+ @Module({
13
+ controllers: [ApiDocsController, ConfigurationController, LoggersController, MetricsController],
14
+ providers: [
15
+ ApiDocsService,
16
+ MetricsService,
17
+ // Records the requests of every controller in the metrics.
18
+ { provide: APP_INTERCEPTOR, useClass: MetricsInterceptor },
19
+ ],
20
+ })
21
+ export class ManagementModule {}
@@ -0,0 +1,85 @@
1
+ import { Controller, Get, Res, UseGuards } from '@nestjs/common';
2
+ import { ApiExcludeController } from '@nestjs/swagger';
3
+ import { Response } from 'express';
4
+ import { isMainThread, threadId } from 'worker_threads';
5
+
6
+ import { SkipLogging } from '../common/interceptors/logging.interceptor.ts';
7
+ import { JHipsterMetrics, MetricsService } from '../common/metrics.ts';
8
+ import { AuthGuard, Roles, RolesGuard, RoleType } from '../auth/index.ts';
9
+
10
+ /** A thread of the threaddump endpoint of Spring Boot Actuator. */
11
+ export interface Thread {
12
+ threadName: string;
13
+ threadId: number;
14
+ threadState: 'RUNNABLE';
15
+ daemon: boolean;
16
+ priority: number;
17
+ blockedTime: number;
18
+ blockedCount: number;
19
+ waitedTime: number;
20
+ waitedCount: number;
21
+ lockName: null;
22
+ lockOwnerId: number;
23
+ lockOwnerName: null;
24
+ inNative: boolean;
25
+ suspended: boolean;
26
+ stackTrace: [];
27
+ lockedMonitors: [];
28
+ lockedSynchronizers: [];
29
+ lockInfo: null;
30
+ }
31
+
32
+ /**
33
+ * The metrics endpoints of JHipster Spring Boot: jhimetrics and threaddump for the metrics page of the JHipster
34
+ * clients (administrators only), prometheus for the Prometheus of docker/prometheus (public, like JHipster).
35
+ */
36
+ @ApiExcludeController()
37
+ @Controller('management')
38
+ export class MetricsController {
39
+ constructor(private readonly metricsService: MetricsService) {}
40
+
41
+ @Get('/jhimetrics')
42
+ @UseGuards(AuthGuard, RolesGuard)
43
+ @Roles(RoleType.ADMIN)
44
+ jhimetrics(): JHipsterMetrics {
45
+ return this.metricsService.jhipsterMetrics();
46
+ }
47
+
48
+ /** The thread running the JavaScript of the application: Node.js runs it on a single thread, its event loop. */
49
+ @Get('/threaddump')
50
+ @UseGuards(AuthGuard, RolesGuard)
51
+ @Roles(RoleType.ADMIN)
52
+ threaddump(): { threads: Thread[] } {
53
+ return {
54
+ threads: [
55
+ {
56
+ threadName: isMainThread ? 'main' : `worker-${threadId}`,
57
+ threadId,
58
+ threadState: 'RUNNABLE',
59
+ daemon: false,
60
+ priority: 5,
61
+ blockedTime: -1,
62
+ blockedCount: 0,
63
+ waitedTime: -1,
64
+ waitedCount: 0,
65
+ lockName: null,
66
+ lockOwnerId: -1,
67
+ lockOwnerName: null,
68
+ inNative: false,
69
+ suspended: false,
70
+ stackTrace: [],
71
+ lockedMonitors: [],
72
+ lockedSynchronizers: [],
73
+ lockInfo: null,
74
+ },
75
+ ],
76
+ };
77
+ }
78
+
79
+ @Get('/prometheus')
80
+ @SkipLogging()
81
+ async prometheus(@Res({ passthrough: true }) response: Response): Promise<string> {
82
+ response.type(this.metricsService.registry.contentType);
83
+ return this.metricsService.prometheus();
84
+ }
85
+ }
@@ -0,0 +1,64 @@
1
+ <%_ if (databaseTypeMongodb) { _%>
2
+ import { MigrationInterface, QueryRunner } from 'typeorm';
3
+
4
+ export class CreateTables1570200270081 implements MigrationInterface {
5
+ // synchronize() builds the schema with its own transaction (or its own connection),
6
+ // so the migration runner must not hold one around it
7
+ transaction = false;
8
+
9
+ public async up(queryRunner: QueryRunner): Promise<any> {
10
+ await queryRunner.dataSource.synchronize();
11
+ }
12
+
13
+ public async down(_queryRunner: QueryRunner): Promise<any> { }
14
+ }
15
+ <%_ } else { _%>
16
+ import type { MigrationInterface, QueryRunner } from 'typeorm';
17
+
18
+ import { <% if (generateBuiltInUserEntity) { %>auditColumns, createJoinTable, <% } %>createTable } from './support/schema.ts';
19
+
20
+ /**
21
+ * The tables of the built-in users and authorities, like the initial schema of JHipster Spring Boot. A database created by
22
+ * a former version of the application ran this migration already, its schema synchronized from the entities.
23
+ */
24
+ export class CreateTables1570200270081 implements MigrationInterface {
25
+ public async up(queryRunner: QueryRunner): Promise<void> {
26
+ <%_ if (generateBuiltInAuthorityEntity) { _%>
27
+ await createTable(queryRunner, '<%= authority.entityTableName %>', [{ name: 'name', type: String, primary: true }]);
28
+ <%_ } _%>
29
+ <%_ if (generateBuiltInUserEntity) { _%>
30
+ await createTable(queryRunner, '<%= user.entityTableName %>', [
31
+ ...auditColumns(queryRunner),
32
+ { name: '<%= nodejsUserPrimaryKeyColumn.name %>', type: <%- nodejsUserPrimaryKeyColumn.type %>, primary: true, generated: '<%= nodejsUserPrimaryKeyColumn.generated %>' },
33
+ { name: 'login', type: String, unique: true },
34
+ { name: 'firstName', type: String, nullable: true },
35
+ { name: 'lastName', type: String, nullable: true },
36
+ { name: 'email', type: String, unique: true },
37
+ { name: 'activated', type: Boolean, default: false },
38
+ { name: 'langKey', type: String, default: 'en' },
39
+ { name: 'password', type: 'varchar'<% if (!generateUserManagement) { %>, nullable: true<% } %> },
40
+ { name: 'imageUrl', type: String, nullable: true },
41
+ { name: 'activationKey', type: String, nullable: true },
42
+ { name: 'resetKey', type: String, nullable: true },
43
+ { name: 'resetDate', type: Date, nullable: true },
44
+ ]);
45
+ await createJoinTable(
46
+ queryRunner,
47
+ '<%= nodejsUserAuthorityJoinTable.name %>',
48
+ { name: '<%= nodejsUserAuthorityJoinTable.joinColumn.name %>', type: <%- nodejsUserAuthorityJoinTable.joinColumn.type %>, referencedTableName: '<%= nodejsUserAuthorityJoinTable.joinColumn.referencedTableName %>', referencedColumnName: '<%= nodejsUserAuthorityJoinTable.joinColumn.referencedColumnName %>' },
49
+ { name: '<%= nodejsUserAuthorityJoinTable.inverseJoinColumn.name %>', type: <%- nodejsUserAuthorityJoinTable.inverseJoinColumn.type %>, referencedTableName: '<%= nodejsUserAuthorityJoinTable.inverseJoinColumn.referencedTableName %>', referencedColumnName: '<%= nodejsUserAuthorityJoinTable.inverseJoinColumn.referencedColumnName %>' },
50
+ );
51
+ <%_ } _%>
52
+ }
53
+
54
+ public async down(queryRunner: QueryRunner): Promise<void> {
55
+ <%_ if (generateBuiltInUserEntity) { _%>
56
+ await queryRunner.dropTable('<%= nodejsUserAuthorityJoinTable.name %>', true);
57
+ await queryRunner.dropTable('<%= user.entityTableName %>', true);
58
+ <%_ } _%>
59
+ <%_ if (generateBuiltInAuthorityEntity) { _%>
60
+ await queryRunner.dropTable('<%= authority.entityTableName %>', true);
61
+ <%_ } _%>
62
+ }
63
+ }
64
+ <%_ } _%>
@@ -0,0 +1,26 @@
1
+ import { type MigrationInterface, type QueryRunner, TableIndex } from 'typeorm';
2
+
3
+ import { createTable } from './support/schema.ts';
4
+
5
+ const SESSION_TABLE = '<%- jhiTablePrefix %>_session';
6
+
7
+ /** The table of the sessions of the server (the Session entity), with the indices of its login and of its expiry. */
8
+ export class CreateSessions1570200270082 implements MigrationInterface {
9
+ public async up(queryRunner: QueryRunner): Promise<void> {
10
+ if (await queryRunner.hasTable(SESSION_TABLE)) return;
11
+ await createTable(queryRunner, SESSION_TABLE, [
12
+ { name: 'sid', type: String, primary: true },
13
+ { name: 'login', type: String, nullable: true },
14
+ { name: 'expires', type: Date },
15
+ { name: 'data', type: 'text' },
16
+ ]);
17
+ const { namingStrategy } = queryRunner.connection;
18
+ for (const column of ['login', 'expires']) {
19
+ await queryRunner.createIndex(SESSION_TABLE, new TableIndex({ name: namingStrategy.indexName(SESSION_TABLE, [column]), columnNames: [column] }));
20
+ }
21
+ }
22
+
23
+ public async down(queryRunner: QueryRunner): Promise<void> {
24
+ await queryRunner.dropTable(SESSION_TABLE, true);
25
+ }
26
+ }
@@ -0,0 +1,74 @@
1
+ <%_ const UserEntity = user.persistClass %>
2
+ import { MigrationInterface, QueryRunner } from 'typeorm';
3
+ <%_ if (authenticationType !== 'oauth2') { _%>
4
+ import { <%= UserEntity %> } from '../users/user.entity.ts';
5
+ import { config } from '../config.ts';
6
+ import { encodePassword } from '../auth/password-util.ts';
7
+ <%_ } _%>
8
+ import { Authority } from '../users/authority.entity.ts';
9
+
10
+ export class SeedUsersRoles1570200490072 implements MigrationInterface {
11
+ role1: Authority = { name: 'ROLE_ADMIN' };
12
+
13
+ role2: Authority = { name: 'ROLE_USER' };
14
+
15
+ <%_ if (authenticationType !== 'oauth2') { _%>
16
+ // The users of JHipster Spring Boot: an administrator and a user, in the native language of the application.
17
+ user1: <%= UserEntity %> = {
18
+ login: 'admin',
19
+ password: 'admin',
20
+ firstName: 'Administrator',
21
+ lastName: 'Administrator',
22
+ email: 'admin@localhost',
23
+ imageUrl: '',
24
+ activated: true,
25
+ langKey: '<%= nativeLanguage %>',
26
+ createdBy: 'system',
27
+ lastModifiedBy: 'system'
28
+ };
29
+
30
+ user2: <%= UserEntity %> = {
31
+ login: 'user',
32
+ password: 'user',
33
+ firstName: 'User',
34
+ lastName: 'User',
35
+ email: 'user@localhost',
36
+ imageUrl: '',
37
+ activated: true,
38
+ langKey: '<%= nativeLanguage %>',
39
+ createdBy: 'system',
40
+ lastModifiedBy: 'system'
41
+ };
42
+
43
+ <%_ } _%>
44
+ public async up(queryRunner: QueryRunner): Promise<any> {
45
+
46
+ const authorityRepository = queryRunner.manager.getRepository("<%- jhiTablePrefix %>_authority");
47
+
48
+ <%_ if (authenticationType === 'oauth2') { _%>
49
+
50
+ await authorityRepository.save([this.role1, this.role2]);
51
+
52
+ <%_ } else { _%>
53
+
54
+ const adminRole = await authorityRepository.save(this.role1);
55
+ const userRole = await authorityRepository.save(this.role2);
56
+
57
+ const userRepository = queryRunner.manager.getRepository("<%- jhiTablePrefix %>_user");
58
+
59
+ this.user1.authorities= [adminRole, userRole];
60
+ this.user2.authorities= [userRole];
61
+
62
+ // The migrations run outside of the dependency injection: the rounds of the configuration, like the PasswordService.
63
+ const rounds = Number(config.get('<%= nodejsPasswordRoundsProperty %>'));
64
+ const users = await Promise.all(
65
+ [this.user1, this.user2].map(async user => ({ ...user, password: await encodePassword(user.password, rounds) })),
66
+ );
67
+
68
+ await userRepository.save(users);
69
+ <%_ } _%>
70
+ }
71
+
72
+
73
+ public async down(_queryRunner: QueryRunner): Promise<any> { }
74
+ }