nestforge-generator 0.1.0

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 (326) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +371 -0
  3. package/README.pt-BR.md +370 -0
  4. package/dist/features/auth-strategy.js +19 -0
  5. package/dist/features/auth-strategy.js.map +1 -0
  6. package/dist/features/database.js +59 -0
  7. package/dist/features/database.js.map +1 -0
  8. package/dist/features/dependencies.js +57 -0
  9. package/dist/features/dependencies.js.map +1 -0
  10. package/dist/features/language.js +172 -0
  11. package/dist/features/language.js.map +1 -0
  12. package/dist/features/markers.js +116 -0
  13. package/dist/features/markers.js.map +1 -0
  14. package/dist/generator.js +120 -0
  15. package/dist/generator.js.map +1 -0
  16. package/dist/index.js +59 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/prompts.js +137 -0
  19. package/dist/prompts.js.map +1 -0
  20. package/package.json +76 -0
  21. package/templates/drizzle/.env.example +45 -0
  22. package/templates/drizzle/.env.test +38 -0
  23. package/templates/drizzle/.github/workflows/ci.yml +119 -0
  24. package/templates/drizzle/ARCHITECTURE.md +227 -0
  25. package/templates/drizzle/ARCHITECTURE.pt-BR.md +225 -0
  26. package/templates/drizzle/CODE_OF_CONDUCT.md +29 -0
  27. package/templates/drizzle/CODE_OF_CONDUCT.pt-BR.md +27 -0
  28. package/templates/drizzle/CONTRIBUTING.md +57 -0
  29. package/templates/drizzle/CONTRIBUTING.pt-BR.md +54 -0
  30. package/templates/drizzle/Dockerfile +43 -0
  31. package/templates/drizzle/LICENSE +21 -0
  32. package/templates/drizzle/README.md +257 -0
  33. package/templates/drizzle/README.pt-BR.md +257 -0
  34. package/templates/drizzle/ROADMAP.md +143 -0
  35. package/templates/drizzle/ROADMAP.pt-BR.md +143 -0
  36. package/templates/drizzle/TESTING.md +136 -0
  37. package/templates/drizzle/TESTING.pt-BR.md +136 -0
  38. package/templates/drizzle/docker-compose.yml +82 -0
  39. package/templates/drizzle/docs/adding-a-module.md +585 -0
  40. package/templates/drizzle/docs/features-markers.md +457 -0
  41. package/templates/drizzle/drizzle.config.ts +49 -0
  42. package/templates/drizzle/nest-cli.json +8 -0
  43. package/templates/drizzle/package.json +96 -0
  44. package/templates/drizzle/src/app.module.ts +72 -0
  45. package/templates/drizzle/src/auth/auth.controller.ts +283 -0
  46. package/templates/drizzle/src/auth/auth.module.ts +81 -0
  47. package/templates/drizzle/src/auth/auth.service.spec.ts +111 -0
  48. package/templates/drizzle/src/auth/auth.service.ts +631 -0
  49. package/templates/drizzle/src/auth/drizzle-session.store.ts +263 -0
  50. package/templates/drizzle/src/auth/dto/forgot-password.dto.ts +9 -0
  51. package/templates/drizzle/src/auth/dto/login.dto.ts +10 -0
  52. package/templates/drizzle/src/auth/dto/refresh-token.dto.ts +9 -0
  53. package/templates/drizzle/src/auth/dto/register.dto.ts +11 -0
  54. package/templates/drizzle/src/auth/dto/reset-password.dto.ts +10 -0
  55. package/templates/drizzle/src/auth/guards/github-auth.guard.ts +10 -0
  56. package/templates/drizzle/src/auth/guards/google-auth.guard.ts +10 -0
  57. package/templates/drizzle/src/auth/guards/jwt-auth.guard.ts +25 -0
  58. package/templates/drizzle/src/auth/guards/session-auth.guard.spec.ts +77 -0
  59. package/templates/drizzle/src/auth/guards/session-auth.guard.ts +37 -0
  60. package/templates/drizzle/src/auth/session.service.spec.ts +74 -0
  61. package/templates/drizzle/src/auth/session.service.ts +96 -0
  62. package/templates/drizzle/src/auth/strategies/github.strategy.ts +41 -0
  63. package/templates/drizzle/src/auth/strategies/google.strategy.ts +43 -0
  64. package/templates/drizzle/src/auth/strategies/jwt.strategy.ts +25 -0
  65. package/templates/drizzle/src/auth/token.service.ts +175 -0
  66. package/templates/drizzle/src/common/constants/permissions.ts +8 -0
  67. package/templates/drizzle/src/common/constants/role-permissions.ts +9 -0
  68. package/templates/drizzle/src/common/constants/role.enum.ts +5 -0
  69. package/templates/drizzle/src/common/decorators/current-user.decorator.ts +8 -0
  70. package/templates/drizzle/src/common/decorators/permissions.decorator.ts +7 -0
  71. package/templates/drizzle/src/common/decorators/public.decorator.ts +4 -0
  72. package/templates/drizzle/src/common/decorators/roles.decorator.ts +6 -0
  73. package/templates/drizzle/src/common/filters/http-exception.filter.ts +34 -0
  74. package/templates/drizzle/src/common/guards/permissions.guard.spec.ts +54 -0
  75. package/templates/drizzle/src/common/guards/permissions.guard.ts +28 -0
  76. package/templates/drizzle/src/common/guards/roles.guard.spec.ts +37 -0
  77. package/templates/drizzle/src/common/guards/roles.guard.ts +24 -0
  78. package/templates/drizzle/src/common/interceptors/logging.interceptor.ts +23 -0
  79. package/templates/drizzle/src/common/middleware/csrf.middleware.spec.ts +97 -0
  80. package/templates/drizzle/src/common/middleware/csrf.middleware.ts +46 -0
  81. package/templates/drizzle/src/common/utils/avatar-storage.util.ts +32 -0
  82. package/templates/drizzle/src/common/utils/hash.util.ts +5 -0
  83. package/templates/drizzle/src/config/env.validation.ts +50 -0
  84. package/templates/drizzle/src/database/database-lifecycle.service.ts +30 -0
  85. package/templates/drizzle/src/database/database.constants.ts +2 -0
  86. package/templates/drizzle/src/database/database.decorators.ts +5 -0
  87. package/templates/drizzle/src/database/database.module.ts +96 -0
  88. package/templates/drizzle/src/database/database.types.ts +28 -0
  89. package/templates/drizzle/src/database/schema/index.ts +11 -0
  90. package/templates/drizzle/src/database/schema/mysql.schema.ts +215 -0
  91. package/templates/drizzle/src/database/schema/postgres.schema.ts +218 -0
  92. package/templates/drizzle/src/database/schema/sqlite.schema.ts +190 -0
  93. package/templates/drizzle/src/database/seed.ts +152 -0
  94. package/templates/drizzle/src/health/health.controller.ts +49 -0
  95. package/templates/drizzle/src/health/health.module.ts +19 -0
  96. package/templates/drizzle/src/health/indicators/drizzle-health.indicator.spec.ts +71 -0
  97. package/templates/drizzle/src/health/indicators/drizzle-health.indicator.ts +52 -0
  98. package/templates/drizzle/src/health/indicators/redis-health.indicator.spec.ts +54 -0
  99. package/templates/drizzle/src/health/indicators/redis-health.indicator.ts +33 -0
  100. package/templates/drizzle/src/mail/mail.module.ts +12 -0
  101. package/templates/drizzle/src/mail/mail.processor.ts +48 -0
  102. package/templates/drizzle/src/mail/mail.service.ts +24 -0
  103. package/templates/drizzle/src/mail/templates/email-templates.ts +28 -0
  104. package/templates/drizzle/src/main.ts +145 -0
  105. package/templates/drizzle/src/metrics/metrics.controller.ts +22 -0
  106. package/templates/drizzle/src/metrics/metrics.interceptor.ts +30 -0
  107. package/templates/drizzle/src/metrics/metrics.module.ts +12 -0
  108. package/templates/drizzle/src/metrics/metrics.service.ts +34 -0
  109. package/templates/drizzle/src/types/express-session.d.ts +13 -0
  110. package/templates/drizzle/src/users/dto/create-user.dto.ts +12 -0
  111. package/templates/drizzle/src/users/dto/find-users-query.dto.ts +12 -0
  112. package/templates/drizzle/src/users/dto/update-user.dto.ts +6 -0
  113. package/templates/drizzle/src/users/entities/user.entity.ts +23 -0
  114. package/templates/drizzle/src/users/users.controller.ts +165 -0
  115. package/templates/drizzle/src/users/users.module.ts +10 -0
  116. package/templates/drizzle/src/users/users.service.spec.ts +301 -0
  117. package/templates/drizzle/src/users/users.service.ts +213 -0
  118. package/templates/drizzle/test/auth.e2e-spec.ts +114 -0
  119. package/templates/drizzle/test/session-auth.e2e-spec.ts +163 -0
  120. package/templates/drizzle/test/users.e2e-spec.ts +212 -0
  121. package/templates/drizzle/test/utils/clean-database.ts +104 -0
  122. package/templates/drizzle/test/utils/e2e-setup.ts +77 -0
  123. package/templates/drizzle/tsconfig.build.json +4 -0
  124. package/templates/drizzle/tsconfig.json +25 -0
  125. package/templates/drizzle/vitest.config.ts +20 -0
  126. package/templates/drizzle/vitest.e2e.config.ts +19 -0
  127. package/templates/prisma/.env.example +44 -0
  128. package/templates/prisma/.env.test +37 -0
  129. package/templates/prisma/.github/workflows/ci.yml +109 -0
  130. package/templates/prisma/ARCHITECTURE.md +70 -0
  131. package/templates/prisma/ARCHITECTURE.pt-BR.md +63 -0
  132. package/templates/prisma/CODE_OF_CONDUCT.md +29 -0
  133. package/templates/prisma/CODE_OF_CONDUCT.pt-BR.md +27 -0
  134. package/templates/prisma/CONTRIBUTING.md +57 -0
  135. package/templates/prisma/CONTRIBUTING.pt-BR.md +54 -0
  136. package/templates/prisma/Dockerfile +33 -0
  137. package/templates/prisma/LICENSE +21 -0
  138. package/templates/prisma/README.md +267 -0
  139. package/templates/prisma/README.pt-BR.md +267 -0
  140. package/templates/prisma/ROADMAP.md +75 -0
  141. package/templates/prisma/ROADMAP.pt-BR.md +65 -0
  142. package/templates/prisma/TESTING.md +123 -0
  143. package/templates/prisma/TESTING.pt-BR.md +123 -0
  144. package/templates/prisma/docker-compose.yml +78 -0
  145. package/templates/prisma/docs/adding-a-module.md +230 -0
  146. package/templates/prisma/docs/features-markers.md +50 -0
  147. package/templates/prisma/nest-cli.json +8 -0
  148. package/templates/prisma/package.json +95 -0
  149. package/templates/prisma/prisma/schema.prisma +105 -0
  150. package/templates/prisma/prisma/seed.ts +46 -0
  151. package/templates/prisma/src/app.module.ts +72 -0
  152. package/templates/prisma/src/auth/auth.controller.ts +283 -0
  153. package/templates/prisma/src/auth/auth.module.ts +59 -0
  154. package/templates/prisma/src/auth/auth.service.spec.ts +53 -0
  155. package/templates/prisma/src/auth/auth.service.ts +224 -0
  156. package/templates/prisma/src/auth/dto/forgot-password.dto.ts +9 -0
  157. package/templates/prisma/src/auth/dto/login.dto.ts +10 -0
  158. package/templates/prisma/src/auth/dto/refresh-token.dto.ts +9 -0
  159. package/templates/prisma/src/auth/dto/register.dto.ts +11 -0
  160. package/templates/prisma/src/auth/dto/reset-password.dto.ts +10 -0
  161. package/templates/prisma/src/auth/guards/github-auth.guard.ts +10 -0
  162. package/templates/prisma/src/auth/guards/google-auth.guard.ts +10 -0
  163. package/templates/prisma/src/auth/guards/jwt-auth.guard.ts +25 -0
  164. package/templates/prisma/src/auth/guards/session-auth.guard.spec.ts +77 -0
  165. package/templates/prisma/src/auth/guards/session-auth.guard.ts +37 -0
  166. package/templates/prisma/src/auth/session.service.spec.ts +74 -0
  167. package/templates/prisma/src/auth/session.service.ts +96 -0
  168. package/templates/prisma/src/auth/strategies/github.strategy.ts +41 -0
  169. package/templates/prisma/src/auth/strategies/google.strategy.ts +43 -0
  170. package/templates/prisma/src/auth/strategies/jwt.strategy.ts +25 -0
  171. package/templates/prisma/src/auth/token.service.ts +84 -0
  172. package/templates/prisma/src/common/constants/permissions.ts +8 -0
  173. package/templates/prisma/src/common/constants/role-permissions.ts +9 -0
  174. package/templates/prisma/src/common/decorators/current-user.decorator.ts +8 -0
  175. package/templates/prisma/src/common/decorators/permissions.decorator.ts +7 -0
  176. package/templates/prisma/src/common/decorators/public.decorator.ts +4 -0
  177. package/templates/prisma/src/common/decorators/roles.decorator.ts +6 -0
  178. package/templates/prisma/src/common/filters/http-exception.filter.ts +34 -0
  179. package/templates/prisma/src/common/guards/permissions.guard.spec.ts +54 -0
  180. package/templates/prisma/src/common/guards/permissions.guard.ts +28 -0
  181. package/templates/prisma/src/common/guards/roles.guard.spec.ts +37 -0
  182. package/templates/prisma/src/common/guards/roles.guard.ts +24 -0
  183. package/templates/prisma/src/common/interceptors/logging.interceptor.ts +23 -0
  184. package/templates/prisma/src/common/middleware/csrf.middleware.spec.ts +97 -0
  185. package/templates/prisma/src/common/middleware/csrf.middleware.ts +46 -0
  186. package/templates/prisma/src/common/utils/avatar-storage.util.ts +32 -0
  187. package/templates/prisma/src/common/utils/hash.util.ts +5 -0
  188. package/templates/prisma/src/config/env.validation.ts +49 -0
  189. package/templates/prisma/src/database/prisma.module.ts +9 -0
  190. package/templates/prisma/src/database/prisma.service.ts +13 -0
  191. package/templates/prisma/src/health/health.controller.ts +49 -0
  192. package/templates/prisma/src/health/health.module.ts +19 -0
  193. package/templates/prisma/src/health/indicators/prisma-health.indicator.spec.ts +22 -0
  194. package/templates/prisma/src/health/indicators/prisma-health.indicator.ts +19 -0
  195. package/templates/prisma/src/health/indicators/redis-health.indicator.spec.ts +54 -0
  196. package/templates/prisma/src/health/indicators/redis-health.indicator.ts +33 -0
  197. package/templates/prisma/src/mail/mail.module.ts +12 -0
  198. package/templates/prisma/src/mail/mail.processor.ts +48 -0
  199. package/templates/prisma/src/mail/mail.service.ts +24 -0
  200. package/templates/prisma/src/mail/templates/email-templates.ts +28 -0
  201. package/templates/prisma/src/main.ts +92 -0
  202. package/templates/prisma/src/metrics/metrics.controller.ts +22 -0
  203. package/templates/prisma/src/metrics/metrics.interceptor.ts +30 -0
  204. package/templates/prisma/src/metrics/metrics.module.ts +12 -0
  205. package/templates/prisma/src/metrics/metrics.service.ts +34 -0
  206. package/templates/prisma/src/types/express-session.d.ts +13 -0
  207. package/templates/prisma/src/users/dto/create-user.dto.ts +12 -0
  208. package/templates/prisma/src/users/dto/find-users-query.dto.ts +12 -0
  209. package/templates/prisma/src/users/dto/update-user.dto.ts +6 -0
  210. package/templates/prisma/src/users/entities/user.entity.ts +20 -0
  211. package/templates/prisma/src/users/users.controller.ts +165 -0
  212. package/templates/prisma/src/users/users.module.ts +10 -0
  213. package/templates/prisma/src/users/users.service.spec.ts +104 -0
  214. package/templates/prisma/src/users/users.service.ts +114 -0
  215. package/templates/prisma/test/auth.e2e-spec.ts +75 -0
  216. package/templates/prisma/test/session-auth.e2e-spec.ts +163 -0
  217. package/templates/prisma/test/users.e2e-spec.ts +106 -0
  218. package/templates/prisma/test/utils/clean-database.ts +14 -0
  219. package/templates/prisma/test/utils/e2e-setup.ts +58 -0
  220. package/templates/prisma/tsconfig.build.json +4 -0
  221. package/templates/prisma/tsconfig.json +25 -0
  222. package/templates/prisma/vitest.config.ts +20 -0
  223. package/templates/prisma/vitest.e2e.config.ts +19 -0
  224. package/templates/typeorm/.env.example +45 -0
  225. package/templates/typeorm/.env.test +38 -0
  226. package/templates/typeorm/.github/workflows/ci.yml +116 -0
  227. package/templates/typeorm/ARCHITECTURE.md +158 -0
  228. package/templates/typeorm/ARCHITECTURE.pt-BR.md +156 -0
  229. package/templates/typeorm/CODE_OF_CONDUCT.md +29 -0
  230. package/templates/typeorm/CODE_OF_CONDUCT.pt-BR.md +27 -0
  231. package/templates/typeorm/CONTRIBUTING.md +57 -0
  232. package/templates/typeorm/CONTRIBUTING.pt-BR.md +54 -0
  233. package/templates/typeorm/Dockerfile +43 -0
  234. package/templates/typeorm/LICENSE +21 -0
  235. package/templates/typeorm/README.md +266 -0
  236. package/templates/typeorm/README.pt-BR.md +266 -0
  237. package/templates/typeorm/ROADMAP.md +79 -0
  238. package/templates/typeorm/ROADMAP.pt-BR.md +67 -0
  239. package/templates/typeorm/TESTING.md +114 -0
  240. package/templates/typeorm/TESTING.pt-BR.md +114 -0
  241. package/templates/typeorm/docker-compose.yml +78 -0
  242. package/templates/typeorm/docs/adding-a-module.md +369 -0
  243. package/templates/typeorm/docs/features-markers.md +50 -0
  244. package/templates/typeorm/nest-cli.json +8 -0
  245. package/templates/typeorm/package.json +98 -0
  246. package/templates/typeorm/src/app.module.ts +72 -0
  247. package/templates/typeorm/src/auth/auth.controller.ts +283 -0
  248. package/templates/typeorm/src/auth/auth.module.ts +86 -0
  249. package/templates/typeorm/src/auth/auth.service.spec.ts +111 -0
  250. package/templates/typeorm/src/auth/auth.service.ts +346 -0
  251. package/templates/typeorm/src/auth/dto/forgot-password.dto.ts +9 -0
  252. package/templates/typeorm/src/auth/dto/login.dto.ts +10 -0
  253. package/templates/typeorm/src/auth/dto/refresh-token.dto.ts +9 -0
  254. package/templates/typeorm/src/auth/dto/register.dto.ts +11 -0
  255. package/templates/typeorm/src/auth/dto/reset-password.dto.ts +10 -0
  256. package/templates/typeorm/src/auth/entities/email-verification-token.entity.ts +69 -0
  257. package/templates/typeorm/src/auth/entities/oauth-account.entity.ts +50 -0
  258. package/templates/typeorm/src/auth/entities/password-reset-token.entity.ts +69 -0
  259. package/templates/typeorm/src/auth/entities/refresh-token.entity.ts +69 -0
  260. package/templates/typeorm/src/auth/entities/session.entity.ts +50 -0
  261. package/templates/typeorm/src/auth/guards/github-auth.guard.ts +10 -0
  262. package/templates/typeorm/src/auth/guards/google-auth.guard.ts +10 -0
  263. package/templates/typeorm/src/auth/guards/jwt-auth.guard.ts +25 -0
  264. package/templates/typeorm/src/auth/guards/session-auth.guard.spec.ts +77 -0
  265. package/templates/typeorm/src/auth/guards/session-auth.guard.ts +37 -0
  266. package/templates/typeorm/src/auth/session.service.spec.ts +74 -0
  267. package/templates/typeorm/src/auth/session.service.ts +96 -0
  268. package/templates/typeorm/src/auth/strategies/github.strategy.ts +41 -0
  269. package/templates/typeorm/src/auth/strategies/google.strategy.ts +43 -0
  270. package/templates/typeorm/src/auth/strategies/jwt.strategy.ts +25 -0
  271. package/templates/typeorm/src/auth/token.service.ts +115 -0
  272. package/templates/typeorm/src/common/constants/permissions.ts +8 -0
  273. package/templates/typeorm/src/common/constants/role-permissions.ts +9 -0
  274. package/templates/typeorm/src/common/constants/role.enum.ts +5 -0
  275. package/templates/typeorm/src/common/decorators/current-user.decorator.ts +8 -0
  276. package/templates/typeorm/src/common/decorators/permissions.decorator.ts +7 -0
  277. package/templates/typeorm/src/common/decorators/public.decorator.ts +4 -0
  278. package/templates/typeorm/src/common/decorators/roles.decorator.ts +6 -0
  279. package/templates/typeorm/src/common/filters/http-exception.filter.ts +34 -0
  280. package/templates/typeorm/src/common/guards/permissions.guard.spec.ts +54 -0
  281. package/templates/typeorm/src/common/guards/permissions.guard.ts +28 -0
  282. package/templates/typeorm/src/common/guards/roles.guard.spec.ts +37 -0
  283. package/templates/typeorm/src/common/guards/roles.guard.ts +24 -0
  284. package/templates/typeorm/src/common/interceptors/logging.interceptor.ts +23 -0
  285. package/templates/typeorm/src/common/middleware/csrf.middleware.spec.ts +97 -0
  286. package/templates/typeorm/src/common/middleware/csrf.middleware.ts +46 -0
  287. package/templates/typeorm/src/common/utils/avatar-storage.util.ts +32 -0
  288. package/templates/typeorm/src/common/utils/hash.util.ts +5 -0
  289. package/templates/typeorm/src/config/env.validation.ts +50 -0
  290. package/templates/typeorm/src/database/data-source.ts +20 -0
  291. package/templates/typeorm/src/database/database.module.ts +39 -0
  292. package/templates/typeorm/src/database/seed.ts +81 -0
  293. package/templates/typeorm/src/database/typeorm-options.ts +49 -0
  294. package/templates/typeorm/src/health/health.controller.ts +49 -0
  295. package/templates/typeorm/src/health/health.module.ts +19 -0
  296. package/templates/typeorm/src/health/indicators/redis-health.indicator.spec.ts +54 -0
  297. package/templates/typeorm/src/health/indicators/redis-health.indicator.ts +33 -0
  298. package/templates/typeorm/src/health/indicators/typeorm-health.indicator.spec.ts +49 -0
  299. package/templates/typeorm/src/health/indicators/typeorm-health.indicator.ts +37 -0
  300. package/templates/typeorm/src/mail/mail.module.ts +12 -0
  301. package/templates/typeorm/src/mail/mail.processor.ts +48 -0
  302. package/templates/typeorm/src/mail/mail.service.ts +24 -0
  303. package/templates/typeorm/src/mail/templates/email-templates.ts +28 -0
  304. package/templates/typeorm/src/main.ts +107 -0
  305. package/templates/typeorm/src/metrics/metrics.controller.ts +22 -0
  306. package/templates/typeorm/src/metrics/metrics.interceptor.ts +30 -0
  307. package/templates/typeorm/src/metrics/metrics.module.ts +12 -0
  308. package/templates/typeorm/src/metrics/metrics.service.ts +34 -0
  309. package/templates/typeorm/src/types/express-session.d.ts +13 -0
  310. package/templates/typeorm/src/users/dto/create-user.dto.ts +12 -0
  311. package/templates/typeorm/src/users/dto/find-users-query.dto.ts +12 -0
  312. package/templates/typeorm/src/users/dto/update-user.dto.ts +6 -0
  313. package/templates/typeorm/src/users/entities/user.entity.ts +113 -0
  314. package/templates/typeorm/src/users/users.controller.ts +165 -0
  315. package/templates/typeorm/src/users/users.module.ts +13 -0
  316. package/templates/typeorm/src/users/users.service.spec.ts +183 -0
  317. package/templates/typeorm/src/users/users.service.ts +124 -0
  318. package/templates/typeorm/test/auth.e2e-spec.ts +111 -0
  319. package/templates/typeorm/test/session-auth.e2e-spec.ts +159 -0
  320. package/templates/typeorm/test/users.e2e-spec.ts +183 -0
  321. package/templates/typeorm/test/utils/clean-database.ts +65 -0
  322. package/templates/typeorm/test/utils/e2e-setup.ts +77 -0
  323. package/templates/typeorm/tsconfig.build.json +4 -0
  324. package/templates/typeorm/tsconfig.json +25 -0
  325. package/templates/typeorm/vitest.config.ts +20 -0
  326. package/templates/typeorm/vitest.e2e.config.ts +19 -0
@@ -0,0 +1,224 @@
1
+ import {
2
+ ConflictException,
3
+ Injectable,
4
+ UnauthorizedException,
5
+ } from '@nestjs/common';
6
+ import * as bcrypt from 'bcryptjs';
7
+ import { createHash, randomBytes } from 'crypto';
8
+ import { PrismaService } from '../database/prisma.service';
9
+ // nestforge:feature:redis,auth:password
10
+ import { MailService } from '../mail/mail.service';
11
+ // nestforge:feature:redis,auth:password:end
12
+ // nestforge:feature:auth:password
13
+ import { RegisterDto } from './dto/register.dto';
14
+ import { LoginDto } from './dto/login.dto';
15
+ // nestforge:feature:auth:password:end
16
+ import { OAuthProfile } from './strategies/google.strategy';
17
+
18
+ @Injectable()
19
+ export class AuthService {
20
+ constructor(
21
+ private readonly prisma: PrismaService,
22
+ // nestforge:feature:redis,auth:password
23
+ private readonly mailService: MailService,
24
+ // nestforge:feature:redis,auth:password:end
25
+ ) { }
26
+
27
+ // nestforge:feature:auth:password
28
+ async register(dto: RegisterDto) {
29
+ const existing = await this.prisma.user.findUnique({
30
+ where: { email: dto.email },
31
+ });
32
+
33
+ if (existing) {
34
+ throw new ConflictException('E-mail já cadastrado');
35
+ }
36
+
37
+ const passwordHash = await bcrypt.hash(dto.password, 10);
38
+
39
+ const user = await this.prisma.user.create({
40
+ data: { name: dto.name, email: dto.email, passwordHash },
41
+ });
42
+
43
+ // nestforge:feature:redis
44
+ await this.sendEmailVerification(user.id, user.email, user.name);
45
+ // nestforge:feature:redis:end
46
+
47
+ return this.toAuthenticatedUser(user);
48
+ }
49
+
50
+ async login(dto: LoginDto) {
51
+ const user = await this.prisma.user.findUnique({
52
+ where: { email: dto.email },
53
+ });
54
+
55
+ if (!user || !user.passwordHash) {
56
+ throw new UnauthorizedException('Credenciais inválidas');
57
+ }
58
+
59
+ if (!(await bcrypt.compare(dto.password, user.passwordHash))) {
60
+ throw new UnauthorizedException('Credenciais inválidas');
61
+ }
62
+
63
+ return this.toAuthenticatedUser(user);
64
+ }
65
+ // nestforge:feature:auth:password:end
66
+
67
+ async validateOAuthLogin(profile: OAuthProfile) {
68
+ const linkedAccount = await this.prisma.oAuthAccount.findUnique({
69
+ where: {
70
+ provider_providerUserId: {
71
+ provider: profile.provider,
72
+ providerUserId: profile.providerUserId,
73
+ },
74
+ },
75
+ include: { user: true },
76
+ });
77
+
78
+ if (linkedAccount) {
79
+ return this.toAuthenticatedUser(linkedAccount.user);
80
+ }
81
+
82
+ let user = await this.prisma.user.findUnique({ where: { email: profile.email } });
83
+
84
+ if (!user) {
85
+ user = await this.prisma.user.create({
86
+ data: {
87
+ name: profile.name,
88
+ email: profile.email,
89
+ emailVerifiedAt: new Date(),
90
+ },
91
+ });
92
+ }
93
+
94
+ await this.prisma.oAuthAccount.create({
95
+ data: {
96
+ provider: profile.provider,
97
+ providerUserId: profile.providerUserId,
98
+ userId: user.id,
99
+ },
100
+ });
101
+
102
+ return this.toAuthenticatedUser(user);
103
+ }
104
+
105
+ // nestforge:feature:redis,auth:password
106
+ async forgotPassword(email: string) {
107
+ const user = await this.prisma.user.findUnique({ where: { email } });
108
+
109
+ // resposta genérica sempre, pra não revelar se o e-mail existe na base
110
+ const genericResponse = {
111
+ message: 'Se o e-mail existir, enviaremos instruções de redefinição de senha',
112
+ };
113
+
114
+ if (!user) {
115
+ return genericResponse;
116
+ }
117
+
118
+ const rawToken = randomBytes(32).toString('hex');
119
+ const expiresAt = new Date();
120
+ expiresAt.setHours(expiresAt.getHours() + 1);
121
+
122
+ await this.prisma.passwordResetToken.create({
123
+ data: {
124
+ tokenHash: this.hashToken(rawToken),
125
+ userId: user.id,
126
+ expiresAt,
127
+ },
128
+ });
129
+
130
+ await this.mailService.queuePasswordResetEmail(user.email, user.name, rawToken);
131
+
132
+ return genericResponse;
133
+ }
134
+
135
+ async resetPassword(rawToken: string, newPassword: string) {
136
+ const tokenHash = this.hashToken(rawToken);
137
+
138
+ const stored = await this.prisma.passwordResetToken.findUnique({
139
+ where: { tokenHash },
140
+ });
141
+
142
+ if (!stored || stored.usedAt || stored.expiresAt < new Date()) {
143
+ throw new UnauthorizedException('Token de redefinição inválido ou expirado');
144
+ }
145
+
146
+ const passwordHash = await bcrypt.hash(newPassword, 10);
147
+
148
+ await this.prisma.$transaction([
149
+ this.prisma.user.update({
150
+ where: { id: stored.userId },
151
+ data: { passwordHash },
152
+ }),
153
+ this.prisma.passwordResetToken.update({
154
+ where: { id: stored.id },
155
+ data: { usedAt: new Date() },
156
+ }),
157
+ // por segurança, revoga todas as sessões ativas ao trocar a senha
158
+ this.prisma.refreshToken.updateMany({
159
+ where: { userId: stored.userId, revokedAt: null },
160
+ data: { revokedAt: new Date() },
161
+ }),
162
+ ]);
163
+
164
+ return { message: 'Senha redefinida com sucesso' };
165
+ }
166
+
167
+ async verifyEmail(rawToken: string) {
168
+ const tokenHash = this.hashToken(rawToken);
169
+
170
+ const stored = await this.prisma.emailVerificationToken.findUnique({
171
+ where: { tokenHash },
172
+ });
173
+
174
+ if (!stored || stored.usedAt || stored.expiresAt < new Date()) {
175
+ throw new UnauthorizedException('Token de verificação inválido ou expirado');
176
+ }
177
+
178
+ await this.prisma.$transaction([
179
+ this.prisma.user.update({
180
+ where: { id: stored.userId },
181
+ data: { emailVerifiedAt: new Date() },
182
+ }),
183
+ this.prisma.emailVerificationToken.update({
184
+ where: { id: stored.id },
185
+ data: { usedAt: new Date() },
186
+ }),
187
+ ]);
188
+
189
+ return { message: 'E-mail verificado com sucesso' };
190
+ }
191
+
192
+ private async sendEmailVerification(userId: string, email: string, name: string) {
193
+ const rawToken = randomBytes(32).toString('hex');
194
+ const expiresAt = new Date();
195
+ expiresAt.setHours(expiresAt.getHours() + 24);
196
+
197
+ await this.prisma.emailVerificationToken.create({
198
+ data: {
199
+ tokenHash: this.hashToken(rawToken),
200
+ userId,
201
+ expiresAt,
202
+ },
203
+ });
204
+
205
+ await this.mailService.queueVerificationEmail(email, name, rawToken);
206
+ }
207
+ // nestforge:feature:redis,auth:password:end
208
+
209
+ private toAuthenticatedUser(user: {
210
+ id: string;
211
+ email: string;
212
+ role: string;
213
+ }) {
214
+ return {
215
+ id: user.id,
216
+ email: user.email,
217
+ role: user.role,
218
+ };
219
+ }
220
+
221
+ private hashToken(token: string): string {
222
+ return createHash('sha256').update(token).digest('hex');
223
+ }
224
+ }
@@ -0,0 +1,9 @@
1
+ // nestforge:feature-file:redis,auth:password
2
+ import { z } from 'zod';
3
+ import { createZodDto } from 'nestjs-zod';
4
+
5
+ export const forgotPasswordSchema = z.object({
6
+ email: z.string().email().describe('E-mail da conta a ser recuperada'),
7
+ });
8
+
9
+ export class ForgotPasswordDto extends createZodDto(forgotPasswordSchema) { }
@@ -0,0 +1,10 @@
1
+ // nestforge:feature-file:auth:password
2
+ import { z } from 'zod';
3
+ import { createZodDto } from 'nestjs-zod';
4
+
5
+ export const loginSchema = z.object({
6
+ email: z.string().email().describe('E-mail do usuário'),
7
+ password: z.string().min(8).describe('Senha'),
8
+ });
9
+
10
+ export class LoginDto extends createZodDto(loginSchema) { }
@@ -0,0 +1,9 @@
1
+ // nestforge:feature-file:auth:token
2
+ import { z } from 'zod';
3
+ import { createZodDto } from 'nestjs-zod';
4
+
5
+ export const refreshTokenSchema = z.object({
6
+ refreshToken: z.string().describe('Refresh token emitido no login'),
7
+ });
8
+
9
+ export class RefreshTokenDto extends createZodDto(refreshTokenSchema) { }
@@ -0,0 +1,11 @@
1
+ // nestforge:feature-file:auth:password
2
+ import { z } from 'zod';
3
+ import { createZodDto } from 'nestjs-zod';
4
+
5
+ export const registerSchema = z.object({
6
+ name: z.string().min(2).describe('Nome completo do usuário'),
7
+ email: z.string().email().describe('E-mail do usuário'),
8
+ password: z.string().min(8).describe('Senha (mínimo 8 caracteres)'),
9
+ });
10
+
11
+ export class RegisterDto extends createZodDto(registerSchema) { }
@@ -0,0 +1,10 @@
1
+ // nestforge:feature-file:redis,auth:password
2
+ import { z } from 'zod';
3
+ import { createZodDto } from 'nestjs-zod';
4
+
5
+ export const resetPasswordSchema = z.object({
6
+ token: z.string().describe('Token recebido por e-mail'),
7
+ password: z.string().min(8).describe('Nova senha (mínimo 8 caracteres)'),
8
+ });
9
+
10
+ export class ResetPasswordDto extends createZodDto(resetPasswordSchema) { }
@@ -0,0 +1,10 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { AuthGuard } from '@nestjs/passport';
3
+
4
+ @Injectable()
5
+ export class GithubAuthGuard extends AuthGuard('github') {
6
+ constructor() {
7
+ // API stateless (Bearer token) — não usamos sessão do Passport/Express
8
+ super({ session: false });
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { AuthGuard } from '@nestjs/passport';
3
+
4
+ @Injectable()
5
+ export class GoogleAuthGuard extends AuthGuard('google') {
6
+ constructor() {
7
+ // API stateless (Bearer token) — não usamos sessão do Passport/Express
8
+ super({ session: false });
9
+ }
10
+ }
@@ -0,0 +1,25 @@
1
+ // nestforge:feature-file:auth:token
2
+ import { ExecutionContext, Injectable } from '@nestjs/common';
3
+ import { Reflector } from '@nestjs/core';
4
+ import { AuthGuard } from '@nestjs/passport';
5
+ import { IS_PUBLIC_KEY } from '../../common/decorators/public.decorator';
6
+
7
+ @Injectable()
8
+ export class JwtAuthGuard extends AuthGuard('jwt') {
9
+ constructor(private reflector: Reflector) {
10
+ super();
11
+ }
12
+
13
+ canActivate(context: ExecutionContext) {
14
+ const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
15
+ context.getHandler(),
16
+ context.getClass(),
17
+ ]);
18
+
19
+ if (isPublic) {
20
+ return true;
21
+ }
22
+
23
+ return super.canActivate(context);
24
+ }
25
+ }
@@ -0,0 +1,77 @@
1
+ // nestforge:feature-file:auth:session
2
+ import {
3
+ ExecutionContext,
4
+ UnauthorizedException,
5
+ } from '@nestjs/common';
6
+ import { Reflector } from '@nestjs/core';
7
+ import { Request } from 'express';
8
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
9
+ import { SessionAuthGuard } from './session-auth.guard';
10
+
11
+ describe('SessionAuthGuard', () => {
12
+ let reflector: {
13
+ getAllAndOverride: ReturnType<typeof vi.fn>;
14
+ };
15
+ let guard: SessionAuthGuard;
16
+
17
+ beforeEach(() => {
18
+ reflector = {
19
+ getAllAndOverride: vi.fn(),
20
+ };
21
+
22
+ guard = new SessionAuthGuard(reflector as unknown as Reflector);
23
+ });
24
+
25
+ function createContext(user?: {
26
+ id: string;
27
+ email: string;
28
+ role: string;
29
+ }) {
30
+ const request = {
31
+ session: {
32
+ user,
33
+ },
34
+ } as unknown as Request;
35
+
36
+ const context = {
37
+ getHandler: vi.fn(),
38
+ getClass: vi.fn(),
39
+ switchToHttp: vi.fn(() => ({
40
+ getRequest: () => request,
41
+ })),
42
+ } as unknown as ExecutionContext;
43
+
44
+ return { context, request };
45
+ }
46
+
47
+ it('permite acesso a rotas públicas sem sessão', () => {
48
+ reflector.getAllAndOverride.mockReturnValue(true);
49
+ const { context } = createContext();
50
+
51
+ expect(guard.canActivate(context)).toBe(true);
52
+ });
53
+
54
+ it('rejeita acesso quando não existe usuário na sessão', () => {
55
+ reflector.getAllAndOverride.mockReturnValue(false);
56
+ const { context } = createContext();
57
+
58
+ expect(() => guard.canActivate(context)).toThrow(
59
+ UnauthorizedException,
60
+ );
61
+ });
62
+
63
+ it('permite acesso e disponibiliza o usuário autenticado', () => {
64
+ reflector.getAllAndOverride.mockReturnValue(false);
65
+
66
+ const user = {
67
+ id: 'user-1',
68
+ email: 'jeiel@example.com',
69
+ role: 'USER',
70
+ };
71
+
72
+ const { context, request } = createContext(user);
73
+
74
+ expect(guard.canActivate(context)).toBe(true);
75
+ expect(request.user).toEqual(user);
76
+ });
77
+ });
@@ -0,0 +1,37 @@
1
+ // nestforge:feature-file:auth:session
2
+ import {
3
+ CanActivate,
4
+ ExecutionContext,
5
+ Injectable,
6
+ UnauthorizedException,
7
+ } from '@nestjs/common';
8
+ import { Reflector } from '@nestjs/core';
9
+ import { Request } from 'express';
10
+ import { IS_PUBLIC_KEY } from '../../common/decorators/public.decorator';
11
+
12
+ @Injectable()
13
+ export class SessionAuthGuard implements CanActivate {
14
+ constructor(private readonly reflector: Reflector) { }
15
+
16
+ canActivate(context: ExecutionContext): boolean {
17
+ const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
18
+ context.getHandler(),
19
+ context.getClass(),
20
+ ]);
21
+
22
+ if (isPublic) {
23
+ return true;
24
+ }
25
+
26
+ const request = context.switchToHttp().getRequest<Request>();
27
+ const user = request.session?.user;
28
+
29
+ if (!user) {
30
+ throw new UnauthorizedException('Sessão inválida ou expirada');
31
+ }
32
+
33
+ request.user = user;
34
+
35
+ return true;
36
+ }
37
+ }
@@ -0,0 +1,74 @@
1
+ // nestforge:feature-file:auth:session
2
+ import { Request } from 'express';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { SessionService, SessionUser } from './session.service';
5
+
6
+ describe('SessionService', () => {
7
+ let sessionService: SessionService;
8
+
9
+ beforeEach(() => {
10
+ sessionService = new SessionService();
11
+ });
12
+
13
+ function createRequest() {
14
+ const session = {
15
+ user: undefined,
16
+ csrfToken: undefined as string | undefined,
17
+ regenerate: vi.fn(
18
+ (callback: (error?: Error) => void) => callback(),
19
+ ),
20
+ save: vi.fn(
21
+ (callback: (error?: Error) => void) => callback(),
22
+ ),
23
+ destroy: vi.fn(
24
+ (callback: (error?: Error) => void) => callback(),
25
+ ),
26
+ };
27
+
28
+ return {
29
+ request: { session } as unknown as Request,
30
+ session,
31
+ };
32
+ }
33
+
34
+ it('regenera, preenche e salva a sessão', async () => {
35
+ const { request, session } = createRequest();
36
+ const user: SessionUser = {
37
+ id: 'user-1',
38
+ email: 'jeiel@example.com',
39
+ role: 'USER',
40
+ };
41
+
42
+ const result = await sessionService.establish(request, user);
43
+
44
+ expect(session.regenerate).toHaveBeenCalledOnce();
45
+ expect(session.user).toEqual(user);
46
+ expect(session.save).toHaveBeenCalledOnce();
47
+ expect(session.csrfToken).toMatch(/^[a-f0-9]{64}$/);
48
+ expect(result).toEqual({
49
+ user,
50
+ csrfToken: session.csrfToken,
51
+ });
52
+ });
53
+
54
+ it('emite e salva um novo token CSRF', async () => {
55
+ const { request, session } = createRequest();
56
+
57
+ const result = await sessionService.issueCsrfToken(request);
58
+
59
+ expect(result.csrfToken).toMatch(/^[a-f0-9]{64}$/);
60
+ expect(session.csrfToken).toBe(result.csrfToken);
61
+ expect(session.save).toHaveBeenCalledOnce();
62
+ });
63
+
64
+ it('destrói a sessão atual', async () => {
65
+ const { request, session } = createRequest();
66
+
67
+ const result = await sessionService.destroy(request);
68
+
69
+ expect(session.destroy).toHaveBeenCalledOnce();
70
+ expect(result).toEqual({
71
+ message: 'Logout realizado com sucesso',
72
+ });
73
+ });
74
+ });
@@ -0,0 +1,96 @@
1
+ // nestforge:feature-file:auth:session
2
+ import {
3
+ Injectable,
4
+ InternalServerErrorException,
5
+ } from '@nestjs/common';
6
+ import { Request } from 'express';
7
+ import { randomBytes } from 'node:crypto';
8
+
9
+ export interface SessionUser {
10
+ id: string;
11
+ email: string;
12
+ role: string;
13
+ }
14
+
15
+ @Injectable()
16
+ export class SessionService {
17
+ async establish(request: Request, user: SessionUser) {
18
+ await this.regenerate(request);
19
+
20
+ const csrfToken = this.generateCsrfToken();
21
+
22
+ request.session.user = user;
23
+ request.session.csrfToken = csrfToken;
24
+
25
+ await this.save(request);
26
+
27
+ return { user, csrfToken };
28
+ }
29
+
30
+ async issueCsrfToken(request: Request) {
31
+ const csrfToken = this.generateCsrfToken();
32
+
33
+ request.session.csrfToken = csrfToken;
34
+
35
+ await this.save(request);
36
+
37
+ return { csrfToken };
38
+ }
39
+
40
+ private generateCsrfToken(): string {
41
+ return randomBytes(32).toString('hex');
42
+ }
43
+
44
+ async destroy(request: Request) {
45
+ await new Promise<void>((resolve, reject) => {
46
+ request.session.destroy((error) => {
47
+ if (error) {
48
+ reject(
49
+ new InternalServerErrorException(
50
+ 'Não foi possível encerrar a sessão',
51
+ ),
52
+ );
53
+ return;
54
+ }
55
+
56
+ resolve();
57
+ });
58
+ });
59
+
60
+ return { message: 'Logout realizado com sucesso' };
61
+ }
62
+
63
+ private async regenerate(request: Request): Promise<void> {
64
+ await new Promise<void>((resolve, reject) => {
65
+ request.session.regenerate((error) => {
66
+ if (error) {
67
+ reject(
68
+ new InternalServerErrorException(
69
+ 'Não foi possível iniciar a sessão',
70
+ ),
71
+ );
72
+ return;
73
+ }
74
+
75
+ resolve();
76
+ });
77
+ });
78
+ }
79
+
80
+ private async save(request: Request): Promise<void> {
81
+ await new Promise<void>((resolve, reject) => {
82
+ request.session.save((error) => {
83
+ if (error) {
84
+ reject(
85
+ new InternalServerErrorException(
86
+ 'Não foi possível salvar a sessão',
87
+ ),
88
+ );
89
+ return;
90
+ }
91
+
92
+ resolve();
93
+ });
94
+ });
95
+ }
96
+ }
@@ -0,0 +1,41 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { PassportStrategy } from '@nestjs/passport';
3
+ import { Strategy, Profile } from 'passport-github2';
4
+ import { OAuthProfile } from './google.strategy';
5
+ import { ConfigService } from '@nestjs/config';
6
+
7
+ @Injectable()
8
+ export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
9
+ constructor(configService: ConfigService) {
10
+ super({
11
+ clientID: configService.getOrThrow<string>('GITHUB_CLIENT_ID'),
12
+ clientSecret: configService.getOrThrow<string>(
13
+ 'GITHUB_CLIENT_SECRET',
14
+ ),
15
+ callbackURL: `${configService.getOrThrow<string>(
16
+ 'APP_URL',
17
+ )}/auth/github/callback`,
18
+ scope: ['user:email'],
19
+ });
20
+ }
21
+
22
+ async validate(
23
+ _accessToken: string,
24
+ _refreshToken: string,
25
+ profile: Profile,
26
+ done: (error: unknown, user?: OAuthProfile) => void,
27
+ ) {
28
+ // o e-mail pode vir vazio se o usuário deixou o e-mail privado no GitHub
29
+ const email =
30
+ profile.emails?.[0]?.value ?? `${profile.username}@users.noreply.github.com`;
31
+
32
+ const oauthProfile: OAuthProfile = {
33
+ provider: 'github',
34
+ providerUserId: profile.id,
35
+ email,
36
+ name: profile.displayName || profile.username || 'Usuário GitHub',
37
+ };
38
+
39
+ done(null, oauthProfile);
40
+ }
41
+ }