create-easy-stack 1.0.3

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 (327) hide show
  1. package/bin/create.mjs +244 -0
  2. package/infra/email/files/src/core/config/email.env.ts +73 -0
  3. package/infra/email/files/src/core/infrastructure/email/email-dispatcher.port.ts +13 -0
  4. package/infra/email/files/src/core/infrastructure/email/email-layout.spec.ts +83 -0
  5. package/infra/email/files/src/core/infrastructure/email/email-layout.ts +88 -0
  6. package/infra/email/files/src/core/infrastructure/email/email-sender.factory.spec.ts +87 -0
  7. package/infra/email/files/src/core/infrastructure/email/email-sender.factory.ts +35 -0
  8. package/infra/email/files/src/core/infrastructure/email/email-sender.port.ts +19 -0
  9. package/infra/email/files/src/core/infrastructure/email/email.job.spec.ts +101 -0
  10. package/infra/email/files/src/core/infrastructure/email/email.job.ts +64 -0
  11. package/infra/email/files/src/core/infrastructure/email/email.module.spec.ts +60 -0
  12. package/infra/email/files/src/core/infrastructure/email/email.module.ts +34 -0
  13. package/infra/email/files/src/core/infrastructure/email/log-email-sender.spec.ts +34 -0
  14. package/infra/email/files/src/core/infrastructure/email/log-email-sender.ts +17 -0
  15. package/infra/email/files/src/core/infrastructure/email/smtp-email-sender.spec.ts +99 -0
  16. package/infra/email/files/src/core/infrastructure/email/smtp-email-sender.ts +63 -0
  17. package/infra/email/infra.json +58 -0
  18. package/infra/prisma-postgres/docker.service.yml +12 -0
  19. package/infra/prisma-postgres/files/prisma/schema.prisma +10 -0
  20. package/infra/prisma-postgres/files/prisma.config.mjs +10 -0
  21. package/infra/prisma-postgres/files/src/core/config/database.env.ts +13 -0
  22. package/infra/prisma-postgres/files/src/core/infrastructure/database/database.module.ts +9 -0
  23. package/infra/prisma-postgres/files/src/core/infrastructure/database/prisma.service.ts +26 -0
  24. package/infra/prisma-postgres/infra.json +60 -0
  25. package/infra/rabbitmq/docker.service.yml +16 -0
  26. package/infra/rabbitmq/files/src/core/config/messaging.env.ts +29 -0
  27. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/inline-job-queue.spec.ts +60 -0
  28. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/inline-job-queue.ts +33 -0
  29. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-failure.spec.ts +23 -0
  30. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-failure.ts +19 -0
  31. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-payload-cipher.spec.ts +80 -0
  32. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-payload-cipher.ts +66 -0
  33. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-queue.boundary.spec.ts +34 -0
  34. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-queue.factory.ts +21 -0
  35. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-queue.module.spec.ts +75 -0
  36. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-queue.module.ts +30 -0
  37. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/job-queue.port.ts +59 -0
  38. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/rabbitmq-job-queue.spec.ts +510 -0
  39. package/infra/rabbitmq/files/src/core/infrastructure/messaging/jobs/rabbitmq-job-queue.ts +353 -0
  40. package/infra/rabbitmq/files/src/core/infrastructure/messaging/rabbitmq/rabbitmq.module.ts +9 -0
  41. package/infra/rabbitmq/files/src/core/infrastructure/messaging/rabbitmq/rabbitmq.service.spec.ts +289 -0
  42. package/infra/rabbitmq/files/src/core/infrastructure/messaging/rabbitmq/rabbitmq.service.ts +207 -0
  43. package/infra/rabbitmq/infra.json +55 -0
  44. package/infra/redis/docker.service.yml +10 -0
  45. package/infra/redis/files/src/core/http/decorators/rate-limit.decorator.ts +26 -0
  46. package/infra/redis/files/src/core/http/interceptors/rate-limit.interceptor.spec.ts +180 -0
  47. package/infra/redis/files/src/core/http/interceptors/rate-limit.interceptor.ts +105 -0
  48. package/infra/redis/files/src/core/infrastructure/redis/cache.constants.ts +67 -0
  49. package/infra/redis/files/src/core/infrastructure/redis/idempotency/idempotency.module.ts +9 -0
  50. package/infra/redis/files/src/core/infrastructure/redis/idempotency/idempotency.service.spec.ts +146 -0
  51. package/infra/redis/files/src/core/infrastructure/redis/idempotency/idempotency.service.ts +131 -0
  52. package/infra/redis/files/src/core/infrastructure/redis/lock/lock.module.ts +9 -0
  53. package/infra/redis/files/src/core/infrastructure/redis/lock/lock.service.integration-spec.ts +111 -0
  54. package/infra/redis/files/src/core/infrastructure/redis/lock/lock.service.ts +82 -0
  55. package/infra/redis/files/src/core/infrastructure/redis/rate-limit/rate-limit.module.ts +15 -0
  56. package/infra/redis/files/src/core/infrastructure/redis/rate-limit/rate-limit.service.spec.ts +45 -0
  57. package/infra/redis/files/src/core/infrastructure/redis/rate-limit/rate-limit.service.ts +38 -0
  58. package/infra/redis/files/src/core/infrastructure/redis/redis.module.ts +9 -0
  59. package/infra/redis/files/src/core/infrastructure/redis/redis.service.ts +196 -0
  60. package/infra/redis/infra.json +53 -0
  61. package/infra/storage-cloudinary/files/src/core/config/storage.env.ts +43 -0
  62. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/cloudinary-image-storage.spec.ts +275 -0
  63. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/cloudinary-image-storage.ts +158 -0
  64. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/image-storage.factory.ts +30 -0
  65. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/image-storage.module.ts +17 -0
  66. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/image-storage.port.ts +38 -0
  67. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/image-type.spec.ts +48 -0
  68. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/image-type.ts +25 -0
  69. package/infra/storage-cloudinary/files/src/core/infrastructure/storage/unconfigured-image-storage.ts +19 -0
  70. package/infra/storage-cloudinary/infra.json +73 -0
  71. package/lib/fs-utils.mjs +115 -0
  72. package/lib/registry.mjs +50 -0
  73. package/lib/scaffold.mjs +157 -0
  74. package/package.json +37 -0
  75. package/templates/nest-ddd/files/.env.example +5 -0
  76. package/templates/nest-ddd/files/.husky/commit-msg +1 -0
  77. package/templates/nest-ddd/files/.husky/pre-commit +1 -0
  78. package/templates/nest-ddd/files/.oxlintrc.json +10 -0
  79. package/templates/nest-ddd/files/.prettierrc +4 -0
  80. package/templates/nest-ddd/files/_gitignore +40 -0
  81. package/templates/nest-ddd/files/commitlint.config.js +3 -0
  82. package/templates/nest-ddd/files/nest-cli.json +8 -0
  83. package/templates/nest-ddd/files/package.json +66 -0
  84. package/templates/nest-ddd/files/plop-templates/command/command.ts.hbs +5 -0
  85. package/templates/nest-ddd/files/plop-templates/command/handler.ts.hbs +30 -0
  86. package/templates/nest-ddd/files/plop-templates/module/application/commands/create-{{kebab}}.command.ts.hbs +5 -0
  87. package/templates/nest-ddd/files/plop-templates/module/application/commands/create-{{kebab}}.handler.spec.ts.hbs +35 -0
  88. package/templates/nest-ddd/files/plop-templates/module/application/commands/create-{{kebab}}.handler.ts.hbs +25 -0
  89. package/templates/nest-ddd/files/plop-templates/module/application/index.ts.hbs +5 -0
  90. package/templates/nest-ddd/files/plop-templates/module/application/queries/get-{{kebab}}-by-id.handler.ts.hbs +29 -0
  91. package/templates/nest-ddd/files/plop-templates/module/application/queries/get-{{kebab}}-by-id.query.ts.hbs +5 -0
  92. package/templates/nest-ddd/files/plop-templates/module/domain/entities/{{kebab}}.entity.ts.hbs +37 -0
  93. package/templates/nest-ddd/files/plop-templates/module/domain/events/{{kebab}}-created.event.ts.hbs +10 -0
  94. package/templates/nest-ddd/files/plop-templates/module/domain/exceptions/{{kebab}}.exception.ts.hbs +11 -0
  95. package/templates/nest-ddd/files/plop-templates/module/domain/index.ts.hbs +4 -0
  96. package/templates/nest-ddd/files/plop-templates/module/domain/repositories/{{kebab}}.repository.ts.hbs +7 -0
  97. package/templates/nest-ddd/files/plop-templates/module/presentation/controllers/{{kebab}}.controller.ts.hbs +53 -0
  98. package/templates/nest-ddd/files/plop-templates/module/presentation/dtos/create-{{kebab}}.dto.ts.hbs +10 -0
  99. package/templates/nest-ddd/files/plop-templates/module/presentation/dtos/{{kebab}}-response.dto.ts.hbs +15 -0
  100. package/templates/nest-ddd/files/plop-templates/module/presentation/index.ts.hbs +4 -0
  101. package/templates/nest-ddd/files/plop-templates/module/presentation/mappers/{{kebab}}.mapper.ts.hbs +13 -0
  102. package/templates/nest-ddd/files/plop-templates/module/{{kebab}}.module.ts.hbs +40 -0
  103. package/templates/nest-ddd/files/plop-templates/persistence-memory/infrastructure/database/repositories/in-memory-{{kebab}}.repository.ts.hbs +17 -0
  104. package/templates/nest-ddd/files/plop-templates/persistence-memory/infrastructure/index.ts.hbs +1 -0
  105. package/templates/nest-ddd/files/plop-templates/persistence-prisma/infrastructure/database/mappers/{{kebab}}.mapper.ts.hbs +29 -0
  106. package/templates/nest-ddd/files/plop-templates/persistence-prisma/infrastructure/database/repositories/prisma-{{kebab}}.repository.ts.hbs +28 -0
  107. package/templates/nest-ddd/files/plop-templates/persistence-prisma/infrastructure/index.ts.hbs +2 -0
  108. package/templates/nest-ddd/files/plop-templates/query/handler.ts.hbs +28 -0
  109. package/templates/nest-ddd/files/plop-templates/query/query.ts.hbs +5 -0
  110. package/templates/nest-ddd/files/plop-templates/snippets/error-codes.hbs +14 -0
  111. package/templates/nest-ddd/files/plop-templates/snippets/prisma-model.hbs +7 -0
  112. package/templates/nest-ddd/files/plopfile.mjs +186 -0
  113. package/templates/nest-ddd/files/src/app.controller.ts +13 -0
  114. package/templates/nest-ddd/files/src/app.module.ts +26 -0
  115. package/templates/nest-ddd/files/src/core/config/app.config.ts +9 -0
  116. package/templates/nest-ddd/files/src/core/config/env.validation.ts +30 -0
  117. package/templates/nest-ddd/files/src/core/domain/base/aggregate-root.base.ts +18 -0
  118. package/templates/nest-ddd/files/src/core/domain/base/domain-event.base.ts +7 -0
  119. package/templates/nest-ddd/files/src/core/domain/base/entity.base.ts +36 -0
  120. package/templates/nest-ddd/files/src/core/domain/base/value-object.base.ts +17 -0
  121. package/templates/nest-ddd/files/src/core/domain/exceptions/domain.exception.ts +16 -0
  122. package/templates/nest-ddd/files/src/core/domain/exceptions/error-codes.ts +28 -0
  123. package/templates/nest-ddd/files/src/core/domain/types/pagination.ts +9 -0
  124. package/templates/nest-ddd/files/src/core/http/decorators/client-info.decorator.ts +24 -0
  125. package/templates/nest-ddd/files/src/core/http/decorators/current-user.decorator.ts +19 -0
  126. package/templates/nest-ddd/files/src/core/http/dtos/paginated-response.dto.ts +29 -0
  127. package/templates/nest-ddd/files/src/core/http/dtos/pagination-query.dto.ts +17 -0
  128. package/templates/nest-ddd/files/src/core/http/filters/all-exceptions.filter.spec.ts +163 -0
  129. package/templates/nest-ddd/files/src/core/http/filters/all-exceptions.filter.ts +97 -0
  130. package/templates/nest-ddd/files/src/core/http/guards/max-body-size.guard.spec.ts +47 -0
  131. package/templates/nest-ddd/files/src/core/http/guards/max-body-size.guard.ts +31 -0
  132. package/templates/nest-ddd/files/src/core/http/interceptors/logging.interceptor.ts +37 -0
  133. package/templates/nest-ddd/files/src/core/http/interceptors/response.interceptor.ts +53 -0
  134. package/templates/nest-ddd/files/src/core/http/logging/app.logger.ts +34 -0
  135. package/templates/nest-ddd/files/src/core/index.ts +33 -0
  136. package/templates/nest-ddd/files/src/core/utils/correlation-id.util.spec.ts +29 -0
  137. package/templates/nest-ddd/files/src/core/utils/correlation-id.util.ts +15 -0
  138. package/templates/nest-ddd/files/src/core/utils/hash.util.spec.ts +61 -0
  139. package/templates/nest-ddd/files/src/core/utils/hash.util.ts +47 -0
  140. package/templates/nest-ddd/files/src/core/utils/snowflake.util.spec.ts +40 -0
  141. package/templates/nest-ddd/files/src/core/utils/snowflake.util.ts +99 -0
  142. package/templates/nest-ddd/files/src/core/utils/string.util.spec.ts +20 -0
  143. package/templates/nest-ddd/files/src/core/utils/string.util.ts +21 -0
  144. package/templates/nest-ddd/files/src/core/utils/time.util.spec.ts +25 -0
  145. package/templates/nest-ddd/files/src/core/utils/time.util.ts +25 -0
  146. package/templates/nest-ddd/files/src/core/utils/ttl.util.ts +22 -0
  147. package/templates/nest-ddd/files/src/main.ts +61 -0
  148. package/templates/nest-ddd/files/test/setup.ts +6 -0
  149. package/templates/nest-ddd/files/tsconfig.build.json +9 -0
  150. package/templates/nest-ddd/files/tsconfig.json +28 -0
  151. package/templates/nest-ddd/files/vitest.config.e2e.ts +15 -0
  152. package/templates/nest-ddd/files/vitest.config.ts +16 -0
  153. package/templates/nest-ddd/template.json +8 -0
  154. package/templates/node-express/files/.env.example +28 -0
  155. package/templates/node-express/files/docker-compose.yml +52 -0
  156. package/templates/node-express/files/package-lock.json +11880 -0
  157. package/templates/node-express/files/package.json +43 -0
  158. package/templates/node-express/files/server.js +13 -0
  159. package/templates/node-express/files/src/app.js +72 -0
  160. package/templates/node-express/files/src/auth/authUtils.js +116 -0
  161. package/templates/node-express/files/src/auth/checkAuth.js +61 -0
  162. package/templates/node-express/files/src/bucket_pattern/comment.model.js +27 -0
  163. package/templates/node-express/files/src/bucket_pattern/comment.service.js +50 -0
  164. package/templates/node-express/files/src/config/config.aws-s3.js +28 -0
  165. package/templates/node-express/files/src/config/config.cloudinary.js +13 -0
  166. package/templates/node-express/files/src/config/config.mongodb.js +25 -0
  167. package/templates/node-express/files/src/config/config.multer.js +25 -0
  168. package/templates/node-express/files/src/config/config.nodemailer.js +16 -0
  169. package/templates/node-express/files/src/controllers/access.controller.js +49 -0
  170. package/templates/node-express/files/src/controllers/cart.controller.js +38 -0
  171. package/templates/node-express/files/src/controllers/checkout.controller.js +15 -0
  172. package/templates/node-express/files/src/controllers/comment.controller.js +29 -0
  173. package/templates/node-express/files/src/controllers/discount.controller.js +68 -0
  174. package/templates/node-express/files/src/controllers/email.controller.js +16 -0
  175. package/templates/node-express/files/src/controllers/inventory.controller.js +15 -0
  176. package/templates/node-express/files/src/controllers/notification.controller.js +15 -0
  177. package/templates/node-express/files/src/controllers/product.controller.js +175 -0
  178. package/templates/node-express/files/src/controllers/profile.controller.js +65 -0
  179. package/templates/node-express/files/src/controllers/rbac.controller.js +41 -0
  180. package/templates/node-express/files/src/controllers/template.controller.js +20 -0
  181. package/templates/node-express/files/src/controllers/upload.controller.js +56 -0
  182. package/templates/node-express/files/src/controllers/user.controller.js +36 -0
  183. package/templates/node-express/files/src/core/error.response.js +84 -0
  184. package/templates/node-express/files/src/core/success.response.js +44 -0
  185. package/templates/node-express/files/src/database/init.mongodb.level0.js +16 -0
  186. package/templates/node-express/files/src/database/init.mongodb.level1.js +36 -0
  187. package/templates/node-express/files/src/database/init.redis.js +119 -0
  188. package/templates/node-express/files/src/helpers/asyncHandler.js +7 -0
  189. package/templates/node-express/files/src/helpers/check.connect.js +29 -0
  190. package/templates/node-express/files/src/keys/private_key.pem +28 -0
  191. package/templates/node-express/files/src/keys/public_key.pem +9 -0
  192. package/templates/node-express/files/src/logger/discord.log.js +26 -0
  193. package/templates/node-express/files/src/logger/discord.log.v2.js +56 -0
  194. package/templates/node-express/files/src/logger/mylogger.log.js +130 -0
  195. package/templates/node-express/files/src/logger/winston.log.js +27 -0
  196. package/templates/node-express/files/src/middlewares/index.js +21 -0
  197. package/templates/node-express/files/src/middlewares/rbac.js +37 -0
  198. package/templates/node-express/files/src/middlewares/role.middleware.js +12 -0
  199. package/templates/node-express/files/src/models/KeyToken.model.js +37 -0
  200. package/templates/node-express/files/src/models/apiKey.model.js +31 -0
  201. package/templates/node-express/files/src/models/cart.model.js +47 -0
  202. package/templates/node-express/files/src/models/comment.model.js +43 -0
  203. package/templates/node-express/files/src/models/discount.model.js +92 -0
  204. package/templates/node-express/files/src/models/inventory.model.js +42 -0
  205. package/templates/node-express/files/src/models/notification.model.js +52 -0
  206. package/templates/node-express/files/src/models/order.model.js +68 -0
  207. package/templates/node-express/files/src/models/otp.model.js +32 -0
  208. package/templates/node-express/files/src/models/product.model.js +144 -0
  209. package/templates/node-express/files/src/models/repository/cart.repo.js +71 -0
  210. package/templates/node-express/files/src/models/repository/discount.repo.js +63 -0
  211. package/templates/node-express/files/src/models/repository/inventory.repo.js +45 -0
  212. package/templates/node-express/files/src/models/repository/product.repo.js +145 -0
  213. package/templates/node-express/files/src/models/repository/shop.repo.js +18 -0
  214. package/templates/node-express/files/src/models/repository/spu.repo.js +18 -0
  215. package/templates/node-express/files/src/models/repository/user.repo.js +20 -0
  216. package/templates/node-express/files/src/models/resource.model.js +32 -0
  217. package/templates/node-express/files/src/models/role.model.js +57 -0
  218. package/templates/node-express/files/src/models/shop.model.js +43 -0
  219. package/templates/node-express/files/src/models/sku.model.js +81 -0
  220. package/templates/node-express/files/src/models/spu.model.js +101 -0
  221. package/templates/node-express/files/src/models/template.model.js +25 -0
  222. package/templates/node-express/files/src/models/user.model.js +82 -0
  223. package/templates/node-express/files/src/postman/access.post.http +22 -0
  224. package/templates/node-express/files/src/routes/access/index.route.js +21 -0
  225. package/templates/node-express/files/src/routes/cart/index.route.js +20 -0
  226. package/templates/node-express/files/src/routes/checkout/index.route.js +10 -0
  227. package/templates/node-express/files/src/routes/comment/index.route.js +16 -0
  228. package/templates/node-express/files/src/routes/discount/index.route.js +26 -0
  229. package/templates/node-express/files/src/routes/index.route.js +27 -0
  230. package/templates/node-express/files/src/routes/inventory/index.route.js +11 -0
  231. package/templates/node-express/files/src/routes/notification/index.route.js +14 -0
  232. package/templates/node-express/files/src/routes/product/index.route.js +38 -0
  233. package/templates/node-express/files/src/routes/profile/index.route.js +15 -0
  234. package/templates/node-express/files/src/routes/rbac/index.route.js +21 -0
  235. package/templates/node-express/files/src/routes/template/index.route.js +11 -0
  236. package/templates/node-express/files/src/routes/upload/index.route.js +28 -0
  237. package/templates/node-express/files/src/routes/user/index.route.js +13 -0
  238. package/templates/node-express/files/src/services/access.service.js +257 -0
  239. package/templates/node-express/files/src/services/apiKey.service.js +15 -0
  240. package/templates/node-express/files/src/services/cart.service.js +115 -0
  241. package/templates/node-express/files/src/services/checkout.service.js +218 -0
  242. package/templates/node-express/files/src/services/comment.service.js +185 -0
  243. package/templates/node-express/files/src/services/discount.service.js +282 -0
  244. package/templates/node-express/files/src/services/email.service.js +71 -0
  245. package/templates/node-express/files/src/services/inventory.service.js +35 -0
  246. package/templates/node-express/files/src/services/keyToken.service.js +77 -0
  247. package/templates/node-express/files/src/services/notification.service.js +58 -0
  248. package/templates/node-express/files/src/services/otp.service.js +36 -0
  249. package/templates/node-express/files/src/services/product.service.js +228 -0
  250. package/templates/node-express/files/src/services/rbac.service.js +143 -0
  251. package/templates/node-express/files/src/services/redis.service.js +57 -0
  252. package/templates/node-express/files/src/services/redisPubSub.service.js +72 -0
  253. package/templates/node-express/files/src/services/shop.service.js +21 -0
  254. package/templates/node-express/files/src/services/sku.service.js +52 -0
  255. package/templates/node-express/files/src/services/spu.service.js +89 -0
  256. package/templates/node-express/files/src/services/template.service.js +41 -0
  257. package/templates/node-express/files/src/services/upload.s3.service.js +55 -0
  258. package/templates/node-express/files/src/services/upload.service.js +74 -0
  259. package/templates/node-express/files/src/services/user.service.js +92 -0
  260. package/templates/node-express/files/src/temp/1734420604996-ci-cd.png +0 -0
  261. package/templates/node-express/files/src/temp/1734421222213-cover-fb.jpg +0 -0
  262. package/templates/node-express/files/src/temp/1734421222214-cover-fb-01.jpg +0 -0
  263. package/templates/node-express/files/src/temp/1734421293437-cover-fb-01.jpg +0 -0
  264. package/templates/node-express/files/src/temp/1734421293437-cover-fb.jpg +0 -0
  265. package/templates/node-express/files/src/tests/inventory.test.js +21 -0
  266. package/templates/node-express/files/src/tests/message_queue/kafka/consumer.js +23 -0
  267. package/templates/node-express/files/src/tests/message_queue/kafka/producer.js +21 -0
  268. package/templates/node-express/files/src/tests/message_queue/rabbitMQ/consumer.js +28 -0
  269. package/templates/node-express/files/src/tests/message_queue/rabbitMQ/ordered.consumer.js +26 -0
  270. package/templates/node-express/files/src/tests/message_queue/rabbitMQ/ordered.producer.js +31 -0
  271. package/templates/node-express/files/src/tests/message_queue/rabbitMQ/producer.js +26 -0
  272. package/templates/node-express/files/src/tests/message_queue/rabbitMQ/producerDLX.js +44 -0
  273. package/templates/node-express/files/src/tests/mysql/connect.js +59 -0
  274. package/templates/node-express/files/src/tests/product.test.js +14 -0
  275. package/templates/node-express/files/src/utils/index.js +78 -0
  276. package/templates/node-express/files/src/utils/reasonPhrases.js +344 -0
  277. package/templates/node-express/files/src/utils/statusCodes.js +344 -0
  278. package/templates/node-express/files/src/utils/template.html.js +38 -0
  279. package/templates/node-express/template.json +11 -0
  280. package/templates/react-fsd/files/index.html +13 -0
  281. package/templates/react-fsd/files/package.json +15 -0
  282. package/templates/react-fsd/files/public/favicon.svg +1 -0
  283. package/templates/react-fsd/files/public/icons.svg +24 -0
  284. package/templates/react-fsd/files/src/assets/hero.png +0 -0
  285. package/templates/react-fsd/files/src/assets/typescript.svg +1 -0
  286. package/templates/react-fsd/files/src/assets/vite.svg +1 -0
  287. package/templates/react-fsd/files/src/counter.ts +9 -0
  288. package/templates/react-fsd/files/src/main.ts +60 -0
  289. package/templates/react-fsd/files/src/style.css +296 -0
  290. package/templates/react-fsd/files/tsconfig.json +24 -0
  291. package/templates/react-fsd/template.json +11 -0
  292. package/templates/react-monolithic/files/index.html +13 -0
  293. package/templates/react-monolithic/files/package.json +15 -0
  294. package/templates/react-monolithic/files/public/favicon.svg +1 -0
  295. package/templates/react-monolithic/files/public/icons.svg +24 -0
  296. package/templates/react-monolithic/files/src/assets/hero.png +0 -0
  297. package/templates/react-monolithic/files/src/assets/typescript.svg +1 -0
  298. package/templates/react-monolithic/files/src/assets/vite.svg +1 -0
  299. package/templates/react-monolithic/files/src/counter.ts +9 -0
  300. package/templates/react-monolithic/files/src/main.ts +60 -0
  301. package/templates/react-monolithic/files/src/style.css +296 -0
  302. package/templates/react-monolithic/files/tsconfig.json +24 -0
  303. package/templates/react-monolithic/template.json +11 -0
  304. package/templates/vue-fsd/files/index.html +13 -0
  305. package/templates/vue-fsd/files/package.json +15 -0
  306. package/templates/vue-fsd/files/public/favicon.svg +1 -0
  307. package/templates/vue-fsd/files/public/icons.svg +24 -0
  308. package/templates/vue-fsd/files/src/assets/hero.png +0 -0
  309. package/templates/vue-fsd/files/src/assets/typescript.svg +1 -0
  310. package/templates/vue-fsd/files/src/assets/vite.svg +1 -0
  311. package/templates/vue-fsd/files/src/counter.ts +9 -0
  312. package/templates/vue-fsd/files/src/main.ts +60 -0
  313. package/templates/vue-fsd/files/src/style.css +296 -0
  314. package/templates/vue-fsd/files/tsconfig.json +24 -0
  315. package/templates/vue-fsd/template.json +11 -0
  316. package/templates/vue-monolithic/files/index.html +13 -0
  317. package/templates/vue-monolithic/files/package.json +15 -0
  318. package/templates/vue-monolithic/files/public/favicon.svg +1 -0
  319. package/templates/vue-monolithic/files/public/icons.svg +24 -0
  320. package/templates/vue-monolithic/files/src/assets/hero.png +0 -0
  321. package/templates/vue-monolithic/files/src/assets/typescript.svg +1 -0
  322. package/templates/vue-monolithic/files/src/assets/vite.svg +1 -0
  323. package/templates/vue-monolithic/files/src/counter.ts +9 -0
  324. package/templates/vue-monolithic/files/src/main.ts +60 -0
  325. package/templates/vue-monolithic/files/src/style.css +296 -0
  326. package/templates/vue-monolithic/files/tsconfig.json +24 -0
  327. package/templates/vue-monolithic/template.json +11 -0
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+
3
+ const OtpService = require("./otp.service");
4
+ const TemplateService = require("./template.service");
5
+ const transporter = require("../config/config.nodemailer");
6
+ const { replacePlaceholder } = require("../utils/template.html");
7
+ const { NotFoundError, BadRequestError } = require("../core/error.response");
8
+
9
+ class EmailService {
10
+ static sendEmailUsingThirdParty({
11
+ toEmail = null,
12
+ html = null,
13
+ subject = "Xác thực tài khoản email của bạn",
14
+ }) {
15
+ try {
16
+ // send email using nodemailer
17
+ transporter.sendMail(
18
+ {
19
+ from: "ShopDEV <thuyphuocthinhtpt1@gmail.com>",
20
+ to: toEmail,
21
+ subject,
22
+ html,
23
+ },
24
+ (error, info) => {
25
+ if (error) {
26
+ throw new BadRequestError("Failed to send email");
27
+ }
28
+ console.log("Message sent: %s", info.messageId);
29
+ }
30
+ );
31
+ } catch (error) {
32
+ throw new BadRequestError("Failed to send email");
33
+ }
34
+ }
35
+
36
+ static async sendEmail({ email = null }) {
37
+ try {
38
+ // get token from otp
39
+ const otp = await OtpService.newOtp({ email });
40
+
41
+ // get template
42
+ const template = await TemplateService.getTemplate({
43
+ template_name: "HTML_EMAIL_TOKEN",
44
+ });
45
+
46
+ if (!template) {
47
+ throw new NotFoundError("Template not found");
48
+ }
49
+
50
+ // replace placeholder
51
+ const html = replacePlaceholder(template.template_html, {
52
+ verify_link: `http://localhost:3000/api/v1/user/verify-email?email=${email}&token=${otp.otp_token}`,
53
+ });
54
+
55
+ // send email
56
+ EmailService.sendEmailUsingThirdParty({
57
+ toEmail: email,
58
+ html: html,
59
+ subject: "Xác thực tài khoản email của bạn",
60
+ });
61
+
62
+ return {
63
+ message: "Email sent successfully, please check your email",
64
+ };
65
+ } catch (error) {
66
+ throw new BadRequestError("Failed to send email");
67
+ }
68
+ }
69
+ }
70
+
71
+ module.exports = EmailService;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ const { NotFoundError } = require("../core/error.response");
4
+ const inventoryModel = require("../models/inventory.model");
5
+ const { getProductById } = require("../models/repository/product.repo");
6
+
7
+ class InventoryService {
8
+ static async addStockToInventory({
9
+ stock,
10
+ productId,
11
+ shopId,
12
+ location = "14 Doan Uan, Danang city",
13
+ }) {
14
+ const product = await getProductById({ productId });
15
+ if (!product) {
16
+ throw new NotFoundError("Product does not exist");
17
+ }
18
+ const query = {
19
+ inventory_shop_id: shopId,
20
+ inventory_product_id: productId,
21
+ };
22
+ const updateSet = {
23
+ $inc: {
24
+ inventory_stock: +stock,
25
+ },
26
+ $set: {
27
+ inventory_location: location,
28
+ },
29
+ };
30
+ const options = { upsert: true, new: true };
31
+ return await inventoryModel.findOneAndUpdate(query, updateSet, options);
32
+ }
33
+ }
34
+
35
+ module.exports = InventoryService;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+
3
+ const { Types } = require("mongoose");
4
+ const KeyTokenModel = require("../models/KeyToken.model");
5
+
6
+ class KeyTokenService {
7
+ static createKeyToken = async ({
8
+ userId,
9
+ publicKey,
10
+ privateKey,
11
+ refreshToken,
12
+ }) => {
13
+ try {
14
+ // const tokens = await KeyTokenModel.create({
15
+ // user: userId,
16
+ // publicKey,
17
+ // privateKey,
18
+ // });
19
+ // return tokens ? tokens : null;
20
+ const filter = { user: userId };
21
+ const update = {
22
+ publicKey,
23
+ privateKey,
24
+ refreshTokenUsed: [],
25
+ refreshToken,
26
+ };
27
+ const options = { upsert: true, new: true };
28
+ const tokens = await KeyTokenModel.findOneAndUpdate(
29
+ filter,
30
+ update,
31
+ options
32
+ );
33
+ return tokens ? tokens.publicKey : null;
34
+ } catch (error) {
35
+ return error;
36
+ }
37
+ };
38
+
39
+ static getKeyPair = async ({ userId }) => {
40
+ const keyPair = await KeyTokenModel.findOne({ user: userId });
41
+ return {
42
+ privateKey: keyPair.privateKey,
43
+ publicKey: keyPair.publicKey,
44
+ };
45
+ };
46
+
47
+ static findByUserId = async ({ userId }) => {
48
+ const found = await KeyTokenModel.findOne({
49
+ user: new Types.ObjectId(userId),
50
+ });
51
+ return found;
52
+ };
53
+
54
+ static removeKeyTokenById = async (id) => {
55
+ return await KeyTokenModel.deleteOne({ _id: id });
56
+ };
57
+
58
+ static findByRefreshTokenUsed = async (refreshToken) => {
59
+ return await KeyTokenModel.findOne({
60
+ refreshTokenUsed: refreshToken,
61
+ }).lean();
62
+ };
63
+
64
+ static findByRefreshToken = async (refreshToken) => {
65
+ return await KeyTokenModel.findOne({
66
+ refreshToken: refreshToken,
67
+ });
68
+ };
69
+
70
+ static deleteKeyByUserId = async (userId) => {
71
+ return await KeyTokenModel.findOneAndDelete({
72
+ user: new Types.ObjectId(userId),
73
+ });
74
+ };
75
+ }
76
+
77
+ module.exports = KeyTokenService;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ const notificationModel = require("../models/notification.model");
4
+ const { convertToObjectId } = require("../utils");
5
+
6
+ const NOTI_TYPE_CONTENT = {
7
+ "SHOP-001": "Vừa mới thêm một sản phẩm",
8
+ "ORDER-001": "Đặt hàng thành công",
9
+ "ORDER-002": "Đặt hàng thất bại",
10
+ "ORDER-003": "Giao hàng thành công",
11
+ "PROMOTION-001": "Vừa mới thêm voucher mới",
12
+ };
13
+
14
+ class NotificationService {
15
+ static async pushNotiToSystem({
16
+ type = "SHOP-001",
17
+ receiverId,
18
+ senderId,
19
+ options = {},
20
+ }) {
21
+ let noti_content = NOTI_TYPE_CONTENT[type];
22
+ const newNoti = await notificationModel({
23
+ noti_content,
24
+ noti_type: type,
25
+ noti_receiverId: receiverId,
26
+ noti_senderId: convertToObjectId(senderId),
27
+ noti_options: options,
28
+ });
29
+ return newNoti;
30
+ }
31
+
32
+ static async listNotiByUser({ userId = 1, type = "ALL", isRead = 0 }) {
33
+ const match = {
34
+ noti_receiverId: userId,
35
+ };
36
+ if (type !== "ALL") {
37
+ match["noti_type"] = type;
38
+ }
39
+
40
+ return await notificationModel.aggregate([
41
+ {
42
+ $match: match,
43
+ },
44
+ {
45
+ $project: {
46
+ noti_type: 1,
47
+ noti_senderId: 1,
48
+ noti_receiverId: 1,
49
+ noti_content: 1,
50
+ createdAt: 1,
51
+ noti_options: 1,
52
+ },
53
+ },
54
+ ]);
55
+ }
56
+ }
57
+
58
+ module.exports = NotificationService;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ const { Otp } = require("../models/otp.model");
4
+ const { BadRequestError } = require("../core/error.response");
5
+ const crypto = require("crypto");
6
+
7
+ class OtpService {
8
+ static async generateToken() {
9
+ return crypto.randomInt(0, Math.pow(2, 32));
10
+ }
11
+
12
+ static async newOtp({ email = null }) {
13
+ // generate token
14
+ const token = await OtpService.generateToken();
15
+
16
+ // new otp
17
+ const newOtp = await Otp.create({
18
+ otp_email: email,
19
+ otp_token: token,
20
+ });
21
+
22
+ return newOtp;
23
+ }
24
+
25
+ static async checkOtp({ email = null, token = null }) {
26
+ const otp = await Otp.findOne({ email, otp_token: token }).lean().exec();
27
+ if (!otp) {
28
+ throw new BadRequestError("Invalid token");
29
+ }
30
+ // delete because we only use one time - and otp is valid
31
+ await Otp.deleteOne({ email, otp_token: token });
32
+ return otp;
33
+ }
34
+ }
35
+
36
+ module.exports = OtpService;
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+
3
+ const { BadRequestError } = require("../core/error.response");
4
+ const {
5
+ product,
6
+ clothing,
7
+ electronic,
8
+ furniture,
9
+ } = require("../models/product.model");
10
+ const { insertInventory } = require("../models/repository/inventory.repo");
11
+ const {
12
+ findAllDraftProducts,
13
+ publishProductByShop,
14
+ findAllPublishedProducts,
15
+ unpublishProductByShop,
16
+ searchProductsByUser,
17
+ findAllProducts,
18
+ findDetailProduct,
19
+ updateProductById,
20
+ } = require("../models/repository/product.repo");
21
+ const { removeBadValue, updateNestedObjectParser } = require("../utils");
22
+ const NotificationService = require("./notification.service");
23
+ const TYPES = {
24
+ Electronics: "Electronics",
25
+ Clothing: "Clothing",
26
+ Furniture: "Furniture",
27
+ };
28
+ // Define Factory class to create product
29
+ class ProductFactory {
30
+ static productRegistry = {}; // key : class
31
+
32
+ static registerProductType(type, classRef) {
33
+ ProductFactory.productRegistry[type] = classRef;
34
+ }
35
+
36
+ static async createProduct(type, payload) {
37
+ const productType = ProductFactory.productRegistry[type];
38
+ console.log(">>> productType:::", productType);
39
+ if (!productType)
40
+ throw new BadRequestError(`Invalid Product Type::${type}`);
41
+ return new productType(payload).createProduct();
42
+ }
43
+
44
+ static async findAllDraftProducts({ product_shop, limit = 50, skip = 0 }) {
45
+ const query = { product_shop, isDraft: true };
46
+ return await findAllDraftProducts({ query, limit, skip });
47
+ }
48
+
49
+ static async publishProductByShop({ product_shop, product_id }) {
50
+ return await publishProductByShop({ product_shop, product_id });
51
+ }
52
+
53
+ static async findAllPublishedProducts({
54
+ product_shop,
55
+ limit = 50,
56
+ skip = 0,
57
+ }) {
58
+ const query = { product_shop, isPublished: true };
59
+ return await findAllPublishedProducts({ query, limit, skip });
60
+ }
61
+
62
+ static async unpublishProductByShop({ product_shop, product_id }) {
63
+ return await unpublishProductByShop({ product_shop, product_id });
64
+ }
65
+
66
+ static async searchProducts({ keySearch }) {
67
+ return await searchProductsByUser({ keySearch });
68
+ }
69
+
70
+ static async findAllProducts({
71
+ limit = 50,
72
+ sort = "ctime",
73
+ page = 1,
74
+ filter = { isPublished: true },
75
+ }) {
76
+ return await findAllProducts({
77
+ limit,
78
+ sort,
79
+ page,
80
+ filter,
81
+ select: ["product_name", "product_thumb", "product_price"],
82
+ });
83
+ }
84
+
85
+ static async findDetailProduct({ product_id }) {
86
+ return await findDetailProduct({ product_id, unSelect: ["__v"] });
87
+ }
88
+
89
+ static async updateProduct({ product_type, product_id, bodyUpdate }) {
90
+ const productType = ProductFactory.productRegistry[product_type];
91
+ if (!productType) {
92
+ throw new BadRequestError(`Invalid Product Type::${productType}`);
93
+ }
94
+ console.log(">>> productType:::", productType);
95
+ return new productType(bodyUpdate).updateProduct({
96
+ product_id,
97
+ bodyUpdate,
98
+ });
99
+ }
100
+ }
101
+
102
+ // Define base Product class
103
+ class Product {
104
+ constructor({
105
+ product_name,
106
+ product_thumb,
107
+ product_description,
108
+ product_price,
109
+ product_quantity,
110
+ product_type,
111
+ product_shop,
112
+ product_attributes,
113
+ }) {
114
+ this.product_name = product_name;
115
+ this.product_thumb = product_thumb;
116
+ this.product_description = product_description;
117
+ this.product_price = product_price;
118
+ this.product_quantity = product_quantity;
119
+ this.product_type = product_type;
120
+ this.product_shop = product_shop;
121
+ this.product_attributes = product_attributes;
122
+ }
123
+
124
+ async createProduct(product_id) {
125
+ const newProduct = await product.create({ ...this, _id: product_id });
126
+ if (newProduct) {
127
+ await insertInventory({
128
+ product_id: newProduct._id,
129
+ shop_id: newProduct.product_shop,
130
+ stock: newProduct.product_quantity,
131
+ location: "Danang, Vietnam",
132
+ });
133
+ // noti new product is created here
134
+ // push noti to noti system
135
+ NotificationService.pushNotiToSystem({
136
+ type: "SHOP-001",
137
+ senderId: this.product_attributes,
138
+ receiverId: 1,
139
+ options: {
140
+ product_name: this.product_name,
141
+ shop_name: this.product_shop,
142
+ },
143
+ })
144
+ .then((res) => console.log(res))
145
+ .catch((err) => console.log(err));
146
+ return newProduct;
147
+ }
148
+ }
149
+
150
+ async updateProduct({ product_id, bodyUpdate }) {
151
+ return await updateProductById({ product_id, bodyUpdate, model: product });
152
+ }
153
+ }
154
+
155
+ // Define Clothing
156
+ class Clothing extends Product {
157
+ async createProduct() {
158
+ const newClothing = await clothing.create({
159
+ ...this.product_attributes,
160
+ product_shop: this.product_shop,
161
+ });
162
+ if (!newClothing) {
163
+ throw new BadRequestError("Create new Clothing error");
164
+ }
165
+ const newProduct = await super.createProduct(newClothing._id);
166
+ if (!newProduct) {
167
+ throw new BadRequestError("Create new Product error");
168
+ }
169
+ return newProduct;
170
+ }
171
+
172
+ async updateProduct({ product_id, bodyUpdate }) {
173
+ // 1. Remove undefined, null attributes
174
+ let payload = updateNestedObjectParser(bodyUpdate);
175
+ payload = removeBadValue(payload);
176
+ // 2. Check xem update o cho nao
177
+ if (bodyUpdate.product_attributes) {
178
+ // update child
179
+ await updateProductById({ product_id, payload, model: clothing });
180
+ }
181
+ // update parent
182
+ const updateProduct = await super.updateProduct({ product_id, payload });
183
+ return updateProduct;
184
+ }
185
+ }
186
+
187
+ // Define Electronic
188
+ class Electronics extends Product {
189
+ async createProduct() {
190
+ const newElectronics = await electronic.create({
191
+ ...this.product_attributes,
192
+ product_shop: this.product_shop,
193
+ });
194
+ if (!newElectronics) {
195
+ throw new BadRequestError("Create new Electronics error");
196
+ }
197
+ const newProduct = await super.createProduct(newElectronics._id);
198
+ if (!newProduct) {
199
+ throw new BadRequestError("Create new Product error");
200
+ }
201
+ return newProduct;
202
+ }
203
+ }
204
+
205
+ // Define Furniture
206
+ class Furniture extends Product {
207
+ async createProduct() {
208
+ const newFurniture = await furniture.create({
209
+ ...this.product_attributes,
210
+ product_shop: this.product_shop,
211
+ });
212
+ if (!newFurniture) {
213
+ throw new BadRequestError("Create new Furniture error");
214
+ }
215
+ const newProduct = await super.createProduct(newFurniture._id);
216
+ if (!newProduct) {
217
+ throw new BadRequestError("Create new Product error");
218
+ }
219
+ return newProduct;
220
+ }
221
+ }
222
+
223
+ // Regiter productType
224
+ ProductFactory.registerProductType(TYPES.Clothing, Clothing);
225
+ ProductFactory.registerProductType(TYPES.Electronics, Electronics);
226
+ ProductFactory.registerProductType(TYPES.Furniture, Furniture);
227
+
228
+ module.exports = ProductFactory;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+
3
+ const { BadRequestError } = require("../core/error.response");
4
+ const Resource = require("../models/resource.model");
5
+ const Role = require("../models/role.model");
6
+
7
+ /**
8
+ *
9
+ * @param {string} resource_name
10
+ * @param {string} resource_slug
11
+ * @param {string} resource_description
12
+ * @returns
13
+ */
14
+
15
+ const createResource = async ({
16
+ resource_name = "profile",
17
+ resource_slug = "p000001",
18
+ resource_description = "profile description",
19
+ }) => {
20
+ try {
21
+ // 1. Check if resource already exists
22
+ const foundResource = await Resource.findOne({ resource_slug });
23
+ if (foundResource) {
24
+ throw new BadRequestError("Resource already exists");
25
+ }
26
+ // 2. If not, create new resource
27
+ const newResource = await Resource.create({
28
+ resource_name,
29
+ resource_slug,
30
+ resource_description,
31
+ });
32
+ // 3. Return new resource
33
+ return newResource;
34
+ } catch (error) {
35
+ throw new BadRequestError(error.message);
36
+ }
37
+ };
38
+
39
+ const resourceList = async ({
40
+ userId = 0,
41
+ limit = 30,
42
+ offset = 0,
43
+ search = "",
44
+ }) => {
45
+ try {
46
+ // 1. get list resource
47
+ const resources = await Resource.aggregate([
48
+ {
49
+ $project: {
50
+ _id: 0,
51
+ resource_name: "$resource_name",
52
+ resource_slug: "$resource_slug",
53
+ resource_description: "$resource_description",
54
+ resourceId: "$_id",
55
+ createdAt: 1,
56
+ },
57
+ },
58
+ ]);
59
+ // 3. Return list resource
60
+ return resources;
61
+ } catch (error) {
62
+ throw new BadRequestError(error.message);
63
+ }
64
+ };
65
+
66
+ const createRole = async ({
67
+ role_name = "admin",
68
+ role_slug = "r000001",
69
+ role_description = "admin description",
70
+ role_grants = [],
71
+ }) => {
72
+ try {
73
+ // 1. Check if role already exists
74
+ const foundRole = await Role.findOne({ role_slug });
75
+ if (foundRole) {
76
+ throw new BadRequestError("Role already exists");
77
+ }
78
+ // 2. If not, create new role
79
+ const newRole = await Role.create({
80
+ role_name,
81
+ role_slug,
82
+ role_description,
83
+ role_grants,
84
+ });
85
+ // 3. Return new role
86
+ return newRole;
87
+ } catch (error) {
88
+ throw new BadRequestError(error.message);
89
+ }
90
+ };
91
+
92
+ const roleList = async ({
93
+ userId = 0,
94
+ limit = 30,
95
+ offset = 0,
96
+ search = "",
97
+ }) => {
98
+ try {
99
+ // list roles
100
+ const roles = await Role.aggregate(
101
+ {
102
+ $unwind: "$role_grants",
103
+ },
104
+ {
105
+ $lookup: {
106
+ from: "resources",
107
+ localField: "role_grants.resource_id",
108
+ foreignField: "_id",
109
+ as: "resource",
110
+ },
111
+ },
112
+ {
113
+ $unwind: "$resource",
114
+ },
115
+ {
116
+ $project: {
117
+ role: "$role_name",
118
+ resource: "$resource.resource_name",
119
+ actions: "$role_grants.actions",
120
+ attributes: "$role_grants.attributes",
121
+ },
122
+ },
123
+ {
124
+ $unwind: "$actions",
125
+ },
126
+ {
127
+ $project: {
128
+ _id: 0,
129
+ role: 1,
130
+ resource: 1,
131
+ actions: "$actions",
132
+ attributes: 1,
133
+ },
134
+ }
135
+ );
136
+
137
+ return roles;
138
+ } catch (error) {
139
+ throw new BadRequestError(error.message);
140
+ }
141
+ };
142
+
143
+ module.exports = { createResource, resourceList, createRole, roleList };
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ const redis = require("redis");
4
+ const { promisify } = require("util");
5
+ const { reservationInventory } = require("../models/repository/inventory.repo");
6
+ const redisClient = redis.createClient();
7
+
8
+ redisClient.ping((err, res) => {
9
+ if (err) {
10
+ console.log("err:::error connecting to redis", err);
11
+ } else {
12
+ console.log("res:::connected to redis", res);
13
+ }
14
+ });
15
+
16
+ const pexpire = promisify(redisClient.pExpire).bind(redisClient);
17
+ const setnxAsync = promisify(redisClient.setNX).bind(redisClient);
18
+
19
+ const acquireLock = async (productId, quantity, cartId) => {
20
+ const key = `lock_v2024_${productId}`;
21
+ const retryTimes = 10;
22
+ const expireTime = 3000; // 3 seconds thoi gian tam lock
23
+ for (let index = 0; index < retryTimes.length; index++) {
24
+ // tao mot key, thang nao giu key nay duoc vao thanh toan
25
+ const result = await setnxAsync(key, expireTime);
26
+ console.log("result:::", result);
27
+ if (result === 1) {
28
+ // dang co nguoi nam giu key
29
+ // thao tac voi inventory
30
+ const isReserved = await reservationInventory({
31
+ productId,
32
+ cartId,
33
+ quantity,
34
+ });
35
+ // neu reserve trong kho hang thanh cong => giai phong key de nguoi khac co the su dung
36
+ if (isReserved.modifiedCount > 0) {
37
+ await pexpire(key, expireTime);
38
+ return key;
39
+ }
40
+ return null;
41
+ } else {
42
+ // chua co ai co key
43
+ // thu ket noi lay key 10 lan
44
+ await new Promise((resolve) => setTimeout(resolve, 50));
45
+ }
46
+ }
47
+ };
48
+
49
+ const releaseLock = async (keyLock) => {
50
+ const delAsyncKey = promisify(redisClient.del).bind(redisClient);
51
+ return await delAsyncKey(keyLock);
52
+ };
53
+
54
+ module.exports = {
55
+ acquireLock,
56
+ releaseLock,
57
+ };