@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,83 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { LevelsConsoleLogger, LoggerLevels } from './logging.ts';
3
+
4
+ describe('LoggerLevels', () => {
5
+ it('should give the ROOT level to the loggers without a level of their own', () => {
6
+ const levels = new LoggerLevels();
7
+ levels.configure({ 'logging.level.ROOT': 'warn' });
8
+
9
+ expect(levels.getLevel('AuthService')).toEqual({ configuredLevel: null, effectiveLevel: 'WARN' });
10
+ expect(levels.isEnabled('AuthService', 'warn')).toBe(true);
11
+ expect(levels.isEnabled('AuthService', 'log')).toBe(false);
12
+ });
13
+
14
+ it('should give a logger its level, else the one of its parent', () => {
15
+ const levels = new LoggerLevels();
16
+ levels.configure({ 'logging.level.ROOT': 'INFO', 'logging.level.app': 'DEBUG', 'logging.level.AuthService': 'ERROR' });
17
+
18
+ expect(levels.getLevel('AuthService').effectiveLevel).toBe('ERROR');
19
+ expect(levels.getLevel('app.service')).toEqual({ configuredLevel: null, effectiveLevel: 'DEBUG' });
20
+ expect(levels.isEnabled('app.service', 'debug')).toBe(true);
21
+ expect(levels.isEnabled('app.service', 'verbose')).toBe(false);
22
+ });
23
+
24
+ it('should list the ROOT logger first, then the known loggers by name', () => {
25
+ const levels = new LoggerLevels();
26
+ levels.isEnabled('UserService', 'log');
27
+ levels.setLevel('AuthService', 'DEBUG');
28
+
29
+ expect(Object.keys(levels.getLevels())).toEqual(['ROOT', 'AuthService', 'UserService']);
30
+ });
31
+
32
+ it('should give a logger the level of its parent again when its level is removed', () => {
33
+ const levels = new LoggerLevels();
34
+ levels.setLevel('AuthService', 'TRACE');
35
+ levels.setLevel('AuthService', null);
36
+ levels.setLevel('ROOT', null);
37
+
38
+ expect(levels.getLevel('AuthService')).toEqual({ configuredLevel: null, effectiveLevel: 'INFO' });
39
+ expect(levels.getLevel('ROOT').configuredLevel).toBe('INFO');
40
+ });
41
+
42
+ it('should print nothing for a logger OFF', () => {
43
+ const levels = new LoggerLevels();
44
+ levels.setLevel('Quiet', 'OFF');
45
+
46
+ expect(levels.isEnabled('Quiet', 'fatal')).toBe(false);
47
+ });
48
+ });
49
+
50
+ describe('LevelsConsoleLogger', () => {
51
+ it('should print the messages the level of their logger allows', () => {
52
+ const levels = new LoggerLevels();
53
+ levels.setLevel('Allowed', 'DEBUG');
54
+ const write = vi.spyOn(process.stdout, 'write').mockReturnValue(true);
55
+ try {
56
+ const logger = new LevelsConsoleLogger(levels);
57
+ logger.debug('printed', 'Allowed');
58
+ logger.debug('not printed', 'Other');
59
+ const printed = write.mock.calls.map(([chunk]) => String(chunk));
60
+ expect(printed.some(line => line.includes('printed') && line.includes('Allowed'))).toBe(true);
61
+ expect(printed.some(line => line.includes('not printed'))).toBe(false);
62
+ } finally {
63
+ write.mockRestore();
64
+ }
65
+ });
66
+
67
+ it('should print the stack trace of an error with its message only, not for a logger OFF', () => {
68
+ const levels = new LoggerLevels();
69
+ levels.setLevel('Silent', 'OFF');
70
+ const write = vi.spyOn(process.stderr, 'write').mockReturnValue(true);
71
+ try {
72
+ const logger = new LevelsConsoleLogger(levels);
73
+ logger.error('silenced', 'Error: silenced\n at silencedStack', 'Silent');
74
+ logger.error('failed', 'Error: failed\n at printedStack', 'Printed');
75
+ const printed = write.mock.calls.map(([chunk]) => String(chunk)).join('');
76
+ expect(printed).not.toContain('silenced');
77
+ expect(printed).toContain('failed');
78
+ expect(printed).toContain('printedStack');
79
+ } finally {
80
+ write.mockRestore();
81
+ }
82
+ });
83
+ });
@@ -0,0 +1,129 @@
1
+ import { ConsoleLogger, LogLevel } from '@nestjs/common';
2
+
3
+ /** The levels of Spring Boot, from the most to the least restrictive, as the loggers endpoint gives them. */
4
+ export const LEVELS = ['OFF', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'] as const;
5
+
6
+ export type Level = (typeof LEVELS)[number];
7
+
8
+ /** The root logger, the level of the loggers without a level of their own, like the ROOT of Spring Boot. */
9
+ export const ROOT = 'ROOT';
10
+
11
+ /** The level of Spring Boot a log level of Nest needs to be printed. */
12
+ const NEST_LEVELS: Record<LogLevel, Level> = {
13
+ fatal: 'ERROR',
14
+ error: 'ERROR',
15
+ warn: 'WARN',
16
+ log: 'INFO',
17
+ debug: 'DEBUG',
18
+ verbose: 'TRACE',
19
+ };
20
+
21
+ export interface LoggerLevel {
22
+ configuredLevel: Level | null;
23
+ effectiveLevel: Level;
24
+ }
25
+
26
+ export function isLevel(level: unknown): level is Level {
27
+ return typeof level === 'string' && (LEVELS as readonly string[]).includes(level.toUpperCase());
28
+ }
29
+
30
+ /**
31
+ * The levels of the loggers, like the logging system of Spring Boot: a logger is the context of a Nest logger, like
32
+ * `AuthService`, with the level of its own (`logging.level.AuthService`), else the one of its parent (`a.b` for
33
+ * `a.b.c`), else the one of the ROOT logger. The loggers endpoint reads and changes them while the application runs.
34
+ */
35
+ export class LoggerLevels {
36
+ private readonly configured = new Map<string, Level>([[ROOT, 'INFO']]);
37
+
38
+ /** The loggers that printed something, listed by the loggers endpoint with the configured ones. */
39
+ private readonly known = new Set<string>([ROOT]);
40
+
41
+ /** Sets the levels of the `logging.level.<logger>` properties of the configuration. */
42
+ configure(properties: Record<string, unknown>): void {
43
+ for (const [key, value] of Object.entries(properties)) {
44
+ if (key.startsWith('logging.level.') && isLevel(value)) {
45
+ this.setLevel(key.substring('logging.level.'.length), value.toUpperCase() as Level);
46
+ }
47
+ }
48
+ }
49
+
50
+ /** Sets the level of a logger, or removes it with null (the ROOT logger keeps a level). */
51
+ setLevel(name: string, level: Level | null): void {
52
+ this.known.add(name);
53
+ if (level) {
54
+ this.configured.set(name, level);
55
+ } else if (name !== ROOT) {
56
+ this.configured.delete(name);
57
+ }
58
+ }
59
+
60
+ getLevel(name: string): LoggerLevel {
61
+ return { configuredLevel: this.configured.get(name) ?? null, effectiveLevel: this.effectiveLevel(name) };
62
+ }
63
+
64
+ /** Every known logger by name, the ROOT logger first then by name, like the loggers endpoint of Spring Boot. */
65
+ getLevels(): Record<string, LoggerLevel> {
66
+ const names = [...new Set([...this.known, ...this.configured.keys()])].sort((a, b) =>
67
+ a === ROOT ? -1 : b === ROOT ? 1 : a.localeCompare(b),
68
+ );
69
+ return Object.fromEntries(names.map(name => [name, this.getLevel(name)]));
70
+ }
71
+
72
+ isEnabled(name: string | undefined, logLevel: LogLevel): boolean {
73
+ const logger = name || ROOT;
74
+ this.known.add(logger);
75
+ return LEVELS.indexOf(NEST_LEVELS[logLevel]) <= LEVELS.indexOf(this.effectiveLevel(logger));
76
+ }
77
+
78
+ private effectiveLevel(name: string): Level {
79
+ for (let logger: string | undefined = name; logger; logger = parentOf(logger)) {
80
+ const level = this.configured.get(logger);
81
+ if (level) {
82
+ return level;
83
+ }
84
+ }
85
+ return this.configured.get(ROOT)!;
86
+ }
87
+ }
88
+
89
+ function parentOf(name: string): string | undefined {
90
+ const index = name.lastIndexOf('.');
91
+ return index > 0 ? name.substring(0, index) : undefined;
92
+ }
93
+
94
+ /** The levels of the loggers of the application. */
95
+ export const loggerLevels = new LoggerLevels();
96
+
97
+ /**
98
+ * The logger of the application: prints a message when the level of its logger allows it (loggerLevels), instead of the
99
+ * levels Nest applies to every logger.
100
+ */
101
+ export class LevelsConsoleLogger extends ConsoleLogger {
102
+ /** Whether the messages of the last log were printed: the stack trace of an error is printed with them only. */
103
+ private printed = true;
104
+
105
+ constructor(private readonly levels: LoggerLevels = loggerLevels) {
106
+ super({ logLevels: ['verbose', 'debug', 'log', 'warn', 'error', 'fatal'] });
107
+ }
108
+
109
+ protected printMessages(
110
+ messages: unknown[],
111
+ context?: string,
112
+ logLevel: LogLevel = 'log',
113
+ writeStreamType?: 'stdout' | 'stderr',
114
+ errorStack?: unknown,
115
+ params?: Record<string, any>,
116
+ ): void {
117
+ this.printed = this.levels.isEnabled(context, logLevel);
118
+ if (this.printed) {
119
+ super.printMessages(messages, context, logLevel, writeStreamType, errorStack, params);
120
+ }
121
+ }
122
+
123
+ /** The stack trace of an error, which ConsoleLogger prints after its messages: not printed when they are not (OFF). */
124
+ protected printStackTrace(stack: string): void {
125
+ if (this.printed) {
126
+ super.printStackTrace(stack);
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { MetricsService } from './metrics.ts';
3
+
4
+ describe('MetricsService', () => {
5
+ it('should give the requests by status and by endpoint, like the jhimetrics endpoint', () => {
6
+ const metricsService = new MetricsService();
7
+ metricsService.recordRequest('GET', '/api/regions/:id', 200, 10);
8
+ metricsService.recordRequest('GET', '/api/regions/:id', 200, 30);
9
+ metricsService.recordRequest('POST', '/api/regions', 400, 5);
10
+
11
+ const metrics = metricsService.jhipsterMetrics();
12
+
13
+ expect(metrics['http.server.requests']).toEqual({
14
+ all: { count: 3 },
15
+ percode: { '200': { max: 30, mean: 20, count: 2 }, '400': { max: 5, mean: 5, count: 1 } },
16
+ });
17
+ expect(metrics.services).toEqual({
18
+ '/api/regions/:id': { GET: { max: 30, mean: 20, count: 2 } },
19
+ '/api/regions': { POST: { max: 5, mean: 5, count: 1 } },
20
+ });
21
+ });
22
+
23
+ it('should give the memory of the heap and the processor use', () => {
24
+ const metrics = new MetricsService().jhipsterMetrics();
25
+
26
+ expect(metrics.jvm.heap.used).toBeGreaterThan(0);
27
+ expect(metrics.jvm.heap.max).toBeGreaterThanOrEqual(metrics.jvm.heap.used);
28
+ expect(metrics.processMetrics['system.cpu.count']).toBeGreaterThan(0);
29
+ expect(metrics.processMetrics['process.cpu.usage']).toBeGreaterThanOrEqual(0);
30
+ expect(metrics.processMetrics['process.start.time']).toBeLessThanOrEqual(Date.now());
31
+ });
32
+
33
+ it('should give the requests in the format of Prometheus, like the timer of Micrometer', async () => {
34
+ const metricsService = new MetricsService();
35
+ metricsService.recordRequest('GET', '/api/regions', 200, 250);
36
+
37
+ const prometheus = await metricsService.prometheus();
38
+
39
+ expect(prometheus).toContain('http_server_requests_seconds_count{method="GET",uri="/api/regions",status="200",outcome="SUCCESS"} 1');
40
+ expect(prometheus).toContain('http_server_requests_seconds_max{method="GET",uri="/api/regions",status="200",outcome="SUCCESS"} 0.25');
41
+ expect(prometheus).toContain('process_cpu_user_seconds_total');
42
+ });
43
+ });
@@ -0,0 +1,198 @@
1
+ import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
2
+ import { Request, Response } from 'express';
3
+ import { availableParallelism, cpus, loadavg } from 'os';
4
+ import { Gauge, Registry, Summary, collectDefaultMetrics } from 'prom-client';
5
+ import { Observable } from 'rxjs';
6
+ import { getHeapSpaceStatistics, getHeapStatistics } from 'v8';
7
+
8
+ /** The max, mean and count of requests, in milliseconds, like the timers of the jhimetrics endpoint. */
9
+ export interface MaxMeanCount {
10
+ max: number;
11
+ mean: number;
12
+ count: number;
13
+ }
14
+
15
+ export interface MemoryMetrics {
16
+ committed: number;
17
+ max: number;
18
+ used: number;
19
+ }
20
+
21
+ /**
22
+ * The metrics of the jhimetrics endpoint of JHipster Spring Boot the metrics page of the JHipster clients reads, for a
23
+ * Node.js process: the memory of its V8 heap and of each of its spaces instead of the JVM memory pools, its processor
24
+ * use, and the HTTP requests by status and by endpoint. Without the sections Node.js has no equivalent of (garbage
25
+ * collector, caches, connection pool), which the page shows only when present.
26
+ */
27
+ export interface JHipsterMetrics {
28
+ jvm: Record<string, MemoryMetrics>;
29
+ 'http.server.requests': { all: { count: number }; percode: Record<string, MaxMeanCount> };
30
+ services: Record<string, Record<string, MaxMeanCount>>;
31
+ processMetrics: {
32
+ 'system.cpu.usage': number;
33
+ 'system.cpu.count': number;
34
+ 'system.load.average.1m': number;
35
+ 'process.cpu.usage': number;
36
+ 'process.start.time': number;
37
+ 'process.uptime': number;
38
+ };
39
+ }
40
+
41
+ class Timer {
42
+ count = 0;
43
+ total = 0;
44
+ max = 0;
45
+
46
+ record(milliseconds: number): void {
47
+ this.count += 1;
48
+ this.total += milliseconds;
49
+ this.max = Math.max(this.max, milliseconds);
50
+ }
51
+
52
+ get maxMeanCount(): MaxMeanCount {
53
+ return { max: this.max, mean: this.count ? this.total / this.count : 0, count: this.count };
54
+ }
55
+ }
56
+
57
+ const timerOf = <K>(timers: Map<K, Timer>, key: K): Timer => {
58
+ let timer = timers.get(key);
59
+ if (!timer) {
60
+ timer = new Timer();
61
+ timers.set(key, timer);
62
+ }
63
+ return timer;
64
+ };
65
+
66
+ /**
67
+ * The metrics of the application: the default metrics of prom-client (process, Node.js) and the HTTP requests like
68
+ * the http.server.requests timer of Micrometer, given in the format of Prometheus by the prometheus endpoint and in the
69
+ * one of the jhimetrics endpoint.
70
+ */
71
+ @Injectable()
72
+ export class MetricsService {
73
+ readonly registry = new Registry();
74
+
75
+ private readonly requests: Summary<'method' | 'uri' | 'status' | 'outcome'>;
76
+
77
+ private readonly requestsMax: Gauge<'method' | 'uri' | 'status' | 'outcome'>;
78
+
79
+ private readonly byStatus = new Map<number, Timer>();
80
+
81
+ private readonly byService = new Map<string, Map<string, Timer>>();
82
+
83
+ private readonly maxByLabels = new Map<string, number>();
84
+
85
+ private lastCpu = { time: process.hrtime.bigint(), usage: process.cpuUsage(), system: cpuTimes() };
86
+
87
+ constructor() {
88
+ collectDefaultMetrics({ register: this.registry });
89
+ this.requests = new Summary({
90
+ name: 'http_server_requests_seconds',
91
+ help: 'Duration of the HTTP server requests',
92
+ labelNames: ['method', 'uri', 'status', 'outcome'],
93
+ percentiles: [],
94
+ registers: [this.registry],
95
+ });
96
+ this.requestsMax = new Gauge({
97
+ name: 'http_server_requests_seconds_max',
98
+ help: 'Maximum duration of the HTTP server requests',
99
+ labelNames: ['method', 'uri', 'status', 'outcome'],
100
+ registers: [this.registry],
101
+ });
102
+ }
103
+
104
+ /** Records a request by the path of its route, like `/api/regions/:id`, not by its URL. */
105
+ recordRequest(method: string, uri: string, status: number, milliseconds: number): void {
106
+ const labels = { method, uri, status: String(status), outcome: outcomeOf(status) };
107
+ this.requests.observe(labels, milliseconds / 1000);
108
+ const labelsKey = `${method} ${uri} ${status}`;
109
+ const max = Math.max(this.maxByLabels.get(labelsKey) ?? 0, milliseconds);
110
+ this.maxByLabels.set(labelsKey, max);
111
+ this.requestsMax.set(labels, max / 1000);
112
+ timerOf(this.byStatus, status).record(milliseconds);
113
+ const serviceTimers = this.byService.get(uri) ?? new Map<string, Timer>();
114
+ this.byService.set(uri, serviceTimers);
115
+ timerOf(serviceTimers, method).record(milliseconds);
116
+ }
117
+
118
+ async prometheus(): Promise<string> {
119
+ return this.registry.metrics();
120
+ }
121
+
122
+ jhipsterMetrics(): JHipsterMetrics {
123
+ const heap = getHeapStatistics();
124
+ const jvm: Record<string, MemoryMetrics> = {
125
+ heap: { used: heap.used_heap_size, committed: heap.total_heap_size, max: heap.heap_size_limit },
126
+ };
127
+ // The spaces of the heap, without the ones V8 does not use (nothing reserved and nothing available).
128
+ for (const space of getHeapSpaceStatistics().filter(space => space.space_size > 0 || space.space_available_size > 0)) {
129
+ jvm[space.space_name] = {
130
+ used: space.space_used_size,
131
+ committed: space.space_size,
132
+ max: space.space_size + space.space_available_size,
133
+ };
134
+ }
135
+ const percode = Object.fromEntries([...this.byStatus].map(([status, timer]) => [String(status), timer.maxMeanCount]));
136
+ const services = Object.fromEntries(
137
+ [...this.byService].map(([uri, timers]) => [uri, Object.fromEntries([...timers].map(([method, timer]) => [method, timer.maxMeanCount]))]),
138
+ );
139
+ return {
140
+ jvm,
141
+ 'http.server.requests': { all: { count: [...this.byStatus.values()].reduce((count, timer) => count + timer.count, 0) }, percode },
142
+ services,
143
+ processMetrics: { ...this.cpuUsage(), 'process.start.time': Date.now() - process.uptime() * 1000, 'process.uptime': process.uptime() * 1000 },
144
+ };
145
+ }
146
+
147
+ /** The processor use of the process and of the system since the previous call, between 0 and 1. */
148
+ private cpuUsage(): Pick<JHipsterMetrics['processMetrics'], 'system.cpu.usage' | 'system.cpu.count' | 'system.load.average.1m' | 'process.cpu.usage'> {
149
+ const now = { time: process.hrtime.bigint(), usage: process.cpuUsage(), system: cpuTimes() };
150
+ const count = availableParallelism();
151
+ const elapsedMicroseconds = Number(now.time - this.lastCpu.time) / 1000;
152
+ const processMicroseconds = now.usage.user - this.lastCpu.usage.user + now.usage.system - this.lastCpu.usage.system;
153
+ const systemTotal = now.system.total - this.lastCpu.system.total;
154
+ const systemIdle = now.system.idle - this.lastCpu.system.idle;
155
+ this.lastCpu = now;
156
+ return {
157
+ 'system.cpu.usage': systemTotal > 0 ? (systemTotal - systemIdle) / systemTotal : 0,
158
+ 'system.cpu.count': count,
159
+ 'system.load.average.1m': loadavg()[0],
160
+ 'process.cpu.usage': elapsedMicroseconds > 0 ? Math.min(1, processMicroseconds / elapsedMicroseconds / count) : 0,
161
+ };
162
+ }
163
+ }
164
+
165
+ function cpuTimes(): { total: number; idle: number } {
166
+ return cpus().reduce(
167
+ (times, { times: { user, nice, sys, idle, irq } }) => ({ total: times.total + user + nice + sys + idle + irq, idle: times.idle + idle }),
168
+ { total: 0, idle: 0 },
169
+ );
170
+ }
171
+
172
+ function outcomeOf(status: number): string {
173
+ if (status < 200) return 'INFORMATIONAL';
174
+ if (status < 300) return 'SUCCESS';
175
+ if (status < 400) return 'REDIRECTION';
176
+ return status < 500 ? 'CLIENT_ERROR' : 'SERVER_ERROR';
177
+ }
178
+
179
+ /** Records every request handled by a controller in the MetricsService, once its response is sent. */
180
+ @Injectable()
181
+ export class MetricsInterceptor implements NestInterceptor {
182
+ constructor(private readonly metricsService: MetricsService) {}
183
+
184
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
185
+ if (context.getType() !== 'http') {
186
+ return next.handle();
187
+ }
188
+ const request = context.switchToHttp().getRequest<Request>();
189
+ const response = context.switchToHttp().getResponse<Response>();
190
+ const start = process.hrtime.bigint();
191
+ // Recorded once the response is sent, with the status the exception filters set.
192
+ response.once('finish', () => {
193
+ const uri: string = request.route?.path ?? 'UNKNOWN';
194
+ this.metricsService.recordRequest(request.method, uri, response.statusCode, Number(process.hrtime.bigint() - start) / 1e6);
195
+ });
196
+ return next.handle();
197
+ }
198
+ }
@@ -0,0 +1,8 @@
1
+ import { Request as ExpressRequest, Response } from 'express';
2
+ import { <%= userLoginVmClass%> } from '../users/dto/user.dto.ts';
3
+
4
+ export interface Request extends ExpressRequest {
5
+ user?: <%= userLoginVmClass%>;
6
+ // Express always sets the response of a request
7
+ res: Response;
8
+ }
@@ -0,0 +1,43 @@
1
+ import { EntityMetadata<% if (databaseTypeMongodb) { %>, FindOptionsOrder<% } %> } from 'typeorm';
2
+
3
+ export interface SortableOptions {
4
+ /** Columns of the entity no list can be sorted on, like the password of a user. */
5
+ unsortable?: string[];
6
+ /** The label of each relationship, the field of the other entity a list of the entity displays, like `name`. */
7
+ labels?: Record<string, string>;
8
+ }
9
+
10
+ /**
11
+ * Whether a list of the entity can be sorted on a property, found by introspection of its TypeORM metadata like Spring
12
+ * Data validates the sort of a pageable: a column of the entity, or, for a relationship to one it owns, the id or the label of
13
+ * the other entity (`region.id`, `region.name`). A relationship itself, a collection, another column of the other
14
+ * entity or a deeper path cannot be sorted on.
15
+ */
16
+ export function isSortableProperty(metadata: EntityMetadata, property: string, { unsortable = [], labels = {} }: SortableOptions = {}): boolean {
17
+ const [name, otherProperty, ...deeper] = property.split('.');
18
+ const relation = metadata.findRelationWithPropertyPath(name);
19
+ if (otherProperty === undefined) {
20
+ return relation === undefined && !unsortable.includes(name) && metadata.findColumnWithPropertyPath(name) !== undefined;
21
+ }
22
+ if (relation === undefined || !relation.isOwning || relation.isOneToMany || relation.isManyToMany || deeper.length > 0) {
23
+ return false;
24
+ }
25
+ const otherMetadata = relation.inverseEntityMetadata;
26
+ const isId = otherMetadata.primaryColumns.some(column => column.propertyName === otherProperty);
27
+ return (isId || labels[name] === otherProperty) && otherMetadata.findColumnWithPropertyPath(otherProperty) !== undefined;
28
+ }
29
+ <%_ if (databaseTypeMongodb) { _%>
30
+
31
+ /**
32
+ * The order of a MongoDB query by the names of the fields of the documents: TypeORM sorts a MongoDB query by the keys of
33
+ * the order as given, the names of the properties, and the id of a document is its _id.
34
+ */
35
+ export function documentOrder<T>(metadata: EntityMetadata, order?: FindOptionsOrder<T>): FindOptionsOrder<T> | undefined {
36
+ return (
37
+ order &&
38
+ (Object.fromEntries(
39
+ Object.entries(order).map(([property, direction]) => [metadata.findColumnWithPropertyPath(property)?.databaseName ?? property, direction]),
40
+ ) as FindOptionsOrder<T>)
41
+ );
42
+ }
43
+ <%_ } _%>
@@ -0,0 +1,14 @@
1
+ import { EntityMetadata, ObjectLiteral, QueryDeepPartialEntity } from 'typeorm';
2
+
3
+ /**
4
+ * The properties of an entity an update may write: every property but the columns declared with `update: false`, like
5
+ * the creation audit fields. The MongoDB driver of TypeORM ignores the update: false option and would write those
6
+ * columns.
7
+ */
8
+ export function updatableProperties<Entity extends ObjectLiteral>(metadata: EntityMetadata, entity: Entity): QueryDeepPartialEntity<Entity> {
9
+ const properties: ObjectLiteral = { ...entity };
10
+ for (const column of metadata.columns.filter(column => !column.isUpdate)) {
11
+ delete properties[column.propertyName];
12
+ }
13
+ return properties as QueryDeepPartialEntity<Entity>;
14
+ }
@@ -0,0 +1,118 @@
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
+ <%_ if (serviceDiscoveryEureka) { _%>
15
+ eureka:
16
+ instance:
17
+ prefer-ip-address: true
18
+ client:
19
+ service-url:
20
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
21
+
22
+ <%_ } _%>
23
+ <%_ if (serviceDiscoveryConsul) { _%>
24
+ cloud:
25
+ consul:
26
+ discovery:
27
+ prefer-ip-address: true
28
+
29
+ <%_ } _%>
30
+ # The level of the loggers, like Spring Boot: ROOT for the loggers without a level of their own, a logger by the
31
+ # context of its Nest logger (like AuthService: DEBUG). The logs page of the client changes them while it runs.
32
+ logging:
33
+ level:
34
+ ROOT: DEBUG
35
+
36
+ server:
37
+ port: <%= serverPort %>
38
+ # make sure requests the proxy uri instead of the server one, like JHipster Spring Boot (the proxies of the local networks)
39
+ forward-headers-strategy: native
40
+
41
+ # ===================================================================
42
+ # JHipster specific properties
43
+ #
44
+ # Full reference is available at: https://www.jhipster.tech/common-application-properties/
45
+ # ===================================================================
46
+
47
+ # The database, read by orm.config.ts (TypeORM options): environment variables override each property, like
48
+ # DATABASE_HOST or DATABASE_PASSWORD.
49
+ database:
50
+ <%_ if (nodeDevDatabaseType === 'better-sqlite3') { _%>
51
+ # SQLite, the embedded database of development like H2 in a Spring Boot application.
52
+ type: better-sqlite3
53
+ name: '<%- nodeDevSqliteDatabase %>'
54
+ logging: true
55
+ <%_ } else { _%>
56
+ # The database of docker/<%= databaseTypeSql ? prodDatabaseType : 'mongodb' %>.yml (npm run docker:db:up).
57
+ type: <%= nodeDevDatabaseType %>
58
+ host: 127.0.0.1
59
+ port: <%= nodeDatabasePort %>
60
+ <%_ if (databaseTypeSql) { _%>
61
+ username: <%- devDatabaseUsername %>
62
+ password: '<%- devDatabasePassword %>'
63
+ name: <%- devDatabaseName %>
64
+ <%_ } else { _%>
65
+ name: <%= baseName %>
66
+ <%_ } _%>
67
+ logging: false
68
+ <%_ } _%>
69
+
70
+ <%_ if (generateUserManagement) { _%>
71
+ # The SMTP server of the account mails (activation, creation, password reset), read by the mail module
72
+ # (@nestjs-modules/mailer), like spring.mail of JHipster Spring Boot: environment variables override each property, like MAIL_HOST
73
+ # or MAIL_PASSWORD.
74
+ mail:
75
+ host: localhost
76
+ port: 25
77
+ username:
78
+ password:
79
+
80
+ <%_ } _%>
81
+ jhipster:
82
+ <%_ if (applicationTypeMicroservice) { _%>
83
+ # CORS is disabled by default on microservices, as you should access them through a gateway.
84
+ # If you want to enable it, please uncomment the configuration below.
85
+ # cors:
86
+ # allowed-origins: "http://localhost:9000,https://localhost:9000"
87
+ # allowed-methods: "*"
88
+ # allowed-headers: "*"
89
+ # exposed-headers: "Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params"
90
+ # allow-credentials: true
91
+ # max-age: 1800
92
+ <%_ } else { _%>
93
+ # CORS is only enabled by default with the "dev" profile, like JHipster Spring Boot.
94
+ cors:
95
+ # Allow Ionic for JHipster by default
96
+ allowed-origins: "http://localhost:8100,https://localhost:8100<% if (devServerPortProxy) { %>,http://localhost:<%- devServerPortProxy %>,https://localhost:<%- devServerPortProxy %><% } %><% if (!skipClient) { %>,http://localhost:<%- devServerPort %>,https://localhost:<%- devServerPort %><% } %>"
97
+ # Enable CORS when running in GitHub Codespaces
98
+ allowed-origin-patterns: 'https://*.githubpreview.dev'
99
+ allowed-methods: "*"
100
+ allowed-headers: "*"
101
+ exposed-headers: "Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params"
102
+ allow-credentials: true
103
+ max-age: 1800
104
+ <%_ } _%>
105
+ mail: # specific JHipster mail property, for standard properties see MailProperties
106
+ from: <%= baseName %>@localhost
107
+ base-url: http://127.0.0.1:<%= serverPort %>
108
+
109
+ # ===================================================================
110
+ # Application specific properties
111
+ # Add your own application properties here, see the ApplicationProperties class
112
+ # to have type-safe configuration, like in the JHipsterProperties above
113
+ #
114
+ # More documentation is available at:
115
+ # https://www.jhipster.tech/common-application-properties/
116
+ # ===================================================================
117
+
118
+ # application: