@carlonicora/nestjs-neo4jsonapi 6.7.0 → 6.9.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 (218) hide show
  1. package/dist/bootstrap/app.module.factory.d.ts.map +1 -1
  2. package/dist/bootstrap/app.module.factory.js +1 -0
  3. package/dist/bootstrap/app.module.factory.js.map +1 -1
  4. package/dist/bootstrap/bootstrap.options.d.ts +15 -0
  5. package/dist/bootstrap/bootstrap.options.d.ts.map +1 -1
  6. package/dist/common/constants/core-module.ids.d.ts +1 -0
  7. package/dist/common/constants/core-module.ids.d.ts.map +1 -1
  8. package/dist/common/constants/core-module.ids.js +1 -0
  9. package/dist/common/constants/core-module.ids.js.map +1 -1
  10. package/dist/config/enums/queue.id.d.ts +1 -0
  11. package/dist/config/enums/queue.id.d.ts.map +1 -1
  12. package/dist/config/enums/queue.id.js +1 -0
  13. package/dist/config/enums/queue.id.js.map +1 -1
  14. package/dist/core/blocknote/services/blocknote.service.d.ts +25 -0
  15. package/dist/core/blocknote/services/blocknote.service.d.ts.map +1 -1
  16. package/dist/core/blocknote/services/blocknote.service.js +84 -0
  17. package/dist/core/blocknote/services/blocknote.service.js.map +1 -1
  18. package/dist/foundations/blog/blog.constants.d.ts +17 -0
  19. package/dist/foundations/blog/blog.constants.d.ts.map +1 -0
  20. package/dist/foundations/blog/blog.constants.js +17 -0
  21. package/dist/foundations/blog/blog.constants.js.map +1 -0
  22. package/dist/foundations/blog/blog.module.d.ts +28 -0
  23. package/dist/foundations/blog/blog.module.d.ts.map +1 -0
  24. package/dist/foundations/blog/blog.module.js +143 -0
  25. package/dist/foundations/blog/blog.module.js.map +1 -0
  26. package/dist/foundations/blog/controllers/blog-category-translation.controller.d.ts +17 -0
  27. package/dist/foundations/blog/controllers/blog-category-translation.controller.d.ts.map +1 -0
  28. package/dist/foundations/blog/controllers/blog-category-translation.controller.js +81 -0
  29. package/dist/foundations/blog/controllers/blog-category-translation.controller.js.map +1 -0
  30. package/dist/foundations/blog/controllers/blog-category.controller.d.ts +24 -0
  31. package/dist/foundations/blog/controllers/blog-category.controller.d.ts.map +1 -0
  32. package/dist/foundations/blog/controllers/blog-category.controller.js +125 -0
  33. package/dist/foundations/blog/controllers/blog-category.controller.js.map +1 -0
  34. package/dist/foundations/blog/controllers/blog-post-translation.controller.d.ts +17 -0
  35. package/dist/foundations/blog/controllers/blog-post-translation.controller.d.ts.map +1 -0
  36. package/dist/foundations/blog/controllers/blog-post-translation.controller.js +81 -0
  37. package/dist/foundations/blog/controllers/blog-post-translation.controller.js.map +1 -0
  38. package/dist/foundations/blog/controllers/blog-post.controller.d.ts +28 -0
  39. package/dist/foundations/blog/controllers/blog-post.controller.d.ts.map +1 -0
  40. package/dist/foundations/blog/controllers/blog-post.controller.js +157 -0
  41. package/dist/foundations/blog/controllers/blog-post.controller.js.map +1 -0
  42. package/dist/foundations/blog/controllers/blog-settings.controller.d.ts +16 -0
  43. package/dist/foundations/blog/controllers/blog-settings.controller.d.ts.map +1 -0
  44. package/dist/foundations/blog/controllers/blog-settings.controller.js +73 -0
  45. package/dist/foundations/blog/controllers/blog-settings.controller.js.map +1 -0
  46. package/dist/foundations/blog/controllers/blog.public.controller.d.ts +26 -0
  47. package/dist/foundations/blog/controllers/blog.public.controller.d.ts.map +1 -0
  48. package/dist/foundations/blog/controllers/blog.public.controller.js +117 -0
  49. package/dist/foundations/blog/controllers/blog.public.controller.js.map +1 -0
  50. package/dist/foundations/blog/dtos/blog-category-translation.put.dto.d.ts +16 -0
  51. package/dist/foundations/blog/dtos/blog-category-translation.put.dto.d.ts.map +1 -0
  52. package/dist/foundations/blog/dtos/blog-category-translation.put.dto.js +62 -0
  53. package/dist/foundations/blog/dtos/blog-category-translation.put.dto.js.map +1 -0
  54. package/dist/foundations/blog/dtos/blog-category.dto.d.ts +11 -0
  55. package/dist/foundations/blog/dtos/blog-category.dto.d.ts.map +1 -0
  56. package/dist/foundations/blog/dtos/blog-category.dto.js +45 -0
  57. package/dist/foundations/blog/dtos/blog-category.dto.js.map +1 -0
  58. package/dist/foundations/blog/dtos/blog-category.post.dto.d.ts +16 -0
  59. package/dist/foundations/blog/dtos/blog-category.post.dto.d.ts.map +1 -0
  60. package/dist/foundations/blog/dtos/blog-category.post.dto.js +68 -0
  61. package/dist/foundations/blog/dtos/blog-category.post.dto.js.map +1 -0
  62. package/dist/foundations/blog/dtos/blog-category.put.dto.d.ts +21 -0
  63. package/dist/foundations/blog/dtos/blog-category.put.dto.d.ts.map +1 -0
  64. package/dist/foundations/blog/dtos/blog-category.put.dto.js +76 -0
  65. package/dist/foundations/blog/dtos/blog-category.put.dto.js.map +1 -0
  66. package/dist/foundations/blog/dtos/blog-post-translation.put.dto.d.ts +19 -0
  67. package/dist/foundations/blog/dtos/blog-post-translation.put.dto.d.ts.map +1 -0
  68. package/dist/foundations/blog/dtos/blog-post-translation.put.dto.js +73 -0
  69. package/dist/foundations/blog/dtos/blog-post-translation.put.dto.js.map +1 -0
  70. package/dist/foundations/blog/dtos/blog-post.dto.d.ts +11 -0
  71. package/dist/foundations/blog/dtos/blog-post.dto.d.ts.map +1 -0
  72. package/dist/foundations/blog/dtos/blog-post.dto.js +45 -0
  73. package/dist/foundations/blog/dtos/blog-post.dto.js.map +1 -0
  74. package/dist/foundations/blog/dtos/blog-post.post.dto.d.ts +27 -0
  75. package/dist/foundations/blog/dtos/blog-post.post.dto.d.ts.map +1 -0
  76. package/dist/foundations/blog/dtos/blog-post.post.dto.js +98 -0
  77. package/dist/foundations/blog/dtos/blog-post.post.dto.js.map +1 -0
  78. package/dist/foundations/blog/dtos/blog-post.put.dto.d.ts +30 -0
  79. package/dist/foundations/blog/dtos/blog-post.put.dto.d.ts.map +1 -0
  80. package/dist/foundations/blog/dtos/blog-post.put.dto.js +104 -0
  81. package/dist/foundations/blog/dtos/blog-post.put.dto.js.map +1 -0
  82. package/dist/foundations/blog/dtos/blog-settings.put.dto.d.ts +14 -0
  83. package/dist/foundations/blog/dtos/blog-settings.put.dto.d.ts.map +1 -0
  84. package/dist/foundations/blog/dtos/blog-settings.put.dto.js +52 -0
  85. package/dist/foundations/blog/dtos/blog-settings.put.dto.js.map +1 -0
  86. package/dist/foundations/blog/entities/blog-category-translation.d.ts +36 -0
  87. package/dist/foundations/blog/entities/blog-category-translation.d.ts.map +1 -0
  88. package/dist/foundations/blog/entities/blog-category-translation.js +70 -0
  89. package/dist/foundations/blog/entities/blog-category-translation.js.map +1 -0
  90. package/dist/foundations/blog/entities/blog-category-translation.meta.d.ts +3 -0
  91. package/dist/foundations/blog/entities/blog-category-translation.meta.d.ts.map +1 -0
  92. package/dist/foundations/blog/entities/blog-category-translation.meta.js +10 -0
  93. package/dist/foundations/blog/entities/blog-category-translation.meta.js.map +1 -0
  94. package/dist/foundations/blog/entities/blog-category.d.ts +35 -0
  95. package/dist/foundations/blog/entities/blog-category.d.ts.map +1 -0
  96. package/dist/foundations/blog/entities/blog-category.js +78 -0
  97. package/dist/foundations/blog/entities/blog-category.js.map +1 -0
  98. package/dist/foundations/blog/entities/blog-category.meta.d.ts +3 -0
  99. package/dist/foundations/blog/entities/blog-category.meta.d.ts.map +1 -0
  100. package/dist/foundations/blog/entities/blog-category.meta.js +10 -0
  101. package/dist/foundations/blog/entities/blog-category.meta.js.map +1 -0
  102. package/dist/foundations/blog/entities/blog-post-translation.d.ts +43 -0
  103. package/dist/foundations/blog/entities/blog-post-translation.d.ts.map +1 -0
  104. package/dist/foundations/blog/entities/blog-post-translation.js +91 -0
  105. package/dist/foundations/blog/entities/blog-post-translation.js.map +1 -0
  106. package/dist/foundations/blog/entities/blog-post-translation.meta.d.ts +3 -0
  107. package/dist/foundations/blog/entities/blog-post-translation.meta.d.ts.map +1 -0
  108. package/dist/foundations/blog/entities/blog-post-translation.meta.js +10 -0
  109. package/dist/foundations/blog/entities/blog-post-translation.meta.js.map +1 -0
  110. package/dist/foundations/blog/entities/blog-post.d.ts +63 -0
  111. package/dist/foundations/blog/entities/blog-post.d.ts.map +1 -0
  112. package/dist/foundations/blog/entities/blog-post.js +149 -0
  113. package/dist/foundations/blog/entities/blog-post.js.map +1 -0
  114. package/dist/foundations/blog/entities/blog-post.meta.d.ts +3 -0
  115. package/dist/foundations/blog/entities/blog-post.meta.d.ts.map +1 -0
  116. package/dist/foundations/blog/entities/blog-post.meta.js +10 -0
  117. package/dist/foundations/blog/entities/blog-post.meta.js.map +1 -0
  118. package/dist/foundations/blog/entities/blog-settings.d.ts +18 -0
  119. package/dist/foundations/blog/entities/blog-settings.d.ts.map +1 -0
  120. package/dist/foundations/blog/entities/blog-settings.js +26 -0
  121. package/dist/foundations/blog/entities/blog-settings.js.map +1 -0
  122. package/dist/foundations/blog/entities/blog-settings.meta.d.ts +3 -0
  123. package/dist/foundations/blog/entities/blog-settings.meta.d.ts.map +1 -0
  124. package/dist/foundations/blog/entities/blog-settings.meta.js +10 -0
  125. package/dist/foundations/blog/entities/blog-settings.meta.js.map +1 -0
  126. package/dist/foundations/blog/index.d.ts +34 -0
  127. package/dist/foundations/blog/index.d.ts.map +1 -0
  128. package/dist/foundations/blog/index.js +50 -0
  129. package/dist/foundations/blog/index.js.map +1 -0
  130. package/dist/foundations/blog/interfaces/blog.config.interface.d.ts +22 -0
  131. package/dist/foundations/blog/interfaces/blog.config.interface.d.ts.map +1 -0
  132. package/dist/foundations/blog/interfaces/blog.config.interface.js +10 -0
  133. package/dist/foundations/blog/interfaces/blog.config.interface.js.map +1 -0
  134. package/dist/foundations/blog/processors/blog.processor.d.ts +77 -0
  135. package/dist/foundations/blog/processors/blog.processor.d.ts.map +1 -0
  136. package/dist/foundations/blog/processors/blog.processor.js +404 -0
  137. package/dist/foundations/blog/processors/blog.processor.js.map +1 -0
  138. package/dist/foundations/blog/prompts/blog-summary.prompt.d.ts +6 -0
  139. package/dist/foundations/blog/prompts/blog-summary.prompt.d.ts.map +1 -0
  140. package/dist/foundations/blog/prompts/blog-summary.prompt.js +19 -0
  141. package/dist/foundations/blog/prompts/blog-summary.prompt.js.map +1 -0
  142. package/dist/foundations/blog/prompts/blog-translate.prompt.d.ts +7 -0
  143. package/dist/foundations/blog/prompts/blog-translate.prompt.d.ts.map +1 -0
  144. package/dist/foundations/blog/prompts/blog-translate.prompt.js +24 -0
  145. package/dist/foundations/blog/prompts/blog-translate.prompt.js.map +1 -0
  146. package/dist/foundations/blog/repositories/blog-category-translation.repository.d.ts +78 -0
  147. package/dist/foundations/blog/repositories/blog-category-translation.repository.d.ts.map +1 -0
  148. package/dist/foundations/blog/repositories/blog-category-translation.repository.js +199 -0
  149. package/dist/foundations/blog/repositories/blog-category-translation.repository.js.map +1 -0
  150. package/dist/foundations/blog/repositories/blog-category.repository.d.ts +56 -0
  151. package/dist/foundations/blog/repositories/blog-category.repository.d.ts.map +1 -0
  152. package/dist/foundations/blog/repositories/blog-category.repository.js +166 -0
  153. package/dist/foundations/blog/repositories/blog-category.repository.js.map +1 -0
  154. package/dist/foundations/blog/repositories/blog-post-translation.repository.d.ts +88 -0
  155. package/dist/foundations/blog/repositories/blog-post-translation.repository.d.ts.map +1 -0
  156. package/dist/foundations/blog/repositories/blog-post-translation.repository.js +220 -0
  157. package/dist/foundations/blog/repositories/blog-post-translation.repository.js.map +1 -0
  158. package/dist/foundations/blog/repositories/blog-post.repository.d.ts +118 -0
  159. package/dist/foundations/blog/repositories/blog-post.repository.d.ts.map +1 -0
  160. package/dist/foundations/blog/repositories/blog-post.repository.js +350 -0
  161. package/dist/foundations/blog/repositories/blog-post.repository.js.map +1 -0
  162. package/dist/foundations/blog/repositories/blog-settings.repository.d.ts +21 -0
  163. package/dist/foundations/blog/repositories/blog-settings.repository.d.ts.map +1 -0
  164. package/dist/foundations/blog/repositories/blog-settings.repository.js +60 -0
  165. package/dist/foundations/blog/repositories/blog-settings.repository.js.map +1 -0
  166. package/dist/foundations/blog/services/blog-category-translation.service.d.ts +37 -0
  167. package/dist/foundations/blog/services/blog-category-translation.service.d.ts.map +1 -0
  168. package/dist/foundations/blog/services/blog-category-translation.service.js +97 -0
  169. package/dist/foundations/blog/services/blog-category-translation.service.js.map +1 -0
  170. package/dist/foundations/blog/services/blog-category.service.d.ts +56 -0
  171. package/dist/foundations/blog/services/blog-category.service.d.ts.map +1 -0
  172. package/dist/foundations/blog/services/blog-category.service.js +164 -0
  173. package/dist/foundations/blog/services/blog-category.service.js.map +1 -0
  174. package/dist/foundations/blog/services/blog-post-translation.service.d.ts +44 -0
  175. package/dist/foundations/blog/services/blog-post-translation.service.d.ts.map +1 -0
  176. package/dist/foundations/blog/services/blog-post-translation.service.js +115 -0
  177. package/dist/foundations/blog/services/blog-post-translation.service.js.map +1 -0
  178. package/dist/foundations/blog/services/blog-post.service.d.ts +109 -0
  179. package/dist/foundations/blog/services/blog-post.service.d.ts.map +1 -0
  180. package/dist/foundations/blog/services/blog-post.service.js +276 -0
  181. package/dist/foundations/blog/services/blog-post.service.js.map +1 -0
  182. package/dist/foundations/blog/services/blog-queue.service.d.ts +36 -0
  183. package/dist/foundations/blog/services/blog-queue.service.d.ts.map +1 -0
  184. package/dist/foundations/blog/services/blog-queue.service.js +79 -0
  185. package/dist/foundations/blog/services/blog-queue.service.js.map +1 -0
  186. package/dist/foundations/blog/services/blog-settings.service.d.ts +21 -0
  187. package/dist/foundations/blog/services/blog-settings.service.d.ts.map +1 -0
  188. package/dist/foundations/blog/services/blog-settings.service.js +49 -0
  189. package/dist/foundations/blog/services/blog-settings.service.js.map +1 -0
  190. package/dist/foundations/blog/utils/reading-time.d.ts +5 -0
  191. package/dist/foundations/blog/utils/reading-time.d.ts.map +1 -0
  192. package/dist/foundations/blog/utils/reading-time.js +12 -0
  193. package/dist/foundations/blog/utils/reading-time.js.map +1 -0
  194. package/dist/foundations/blog/utils/slug.d.ts +12 -0
  195. package/dist/foundations/blog/utils/slug.d.ts.map +1 -0
  196. package/dist/foundations/blog/utils/slug.js +36 -0
  197. package/dist/foundations/blog/utils/slug.js.map +1 -0
  198. package/dist/foundations/foundations.modules.d.ts +8 -0
  199. package/dist/foundations/foundations.modules.d.ts.map +1 -1
  200. package/dist/foundations/foundations.modules.js +4 -1
  201. package/dist/foundations/foundations.modules.js.map +1 -1
  202. package/dist/foundations/index.d.ts +1 -0
  203. package/dist/foundations/index.d.ts.map +1 -1
  204. package/dist/foundations/index.js +1 -0
  205. package/dist/foundations/index.js.map +1 -1
  206. package/dist/foundations/stripe/services/stripe.payment.service.d.ts +33 -0
  207. package/dist/foundations/stripe/services/stripe.payment.service.d.ts.map +1 -1
  208. package/dist/foundations/stripe/services/stripe.payment.service.js +68 -0
  209. package/dist/foundations/stripe/services/stripe.payment.service.js.map +1 -1
  210. package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.d.ts +2 -2
  211. package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.d.ts.map +1 -1
  212. package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.js +9 -9
  213. package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.js.map +1 -1
  214. package/dist/foundations/tokenusage/enums/tokenusage.type.d.ts +3 -1
  215. package/dist/foundations/tokenusage/enums/tokenusage.type.d.ts.map +1 -1
  216. package/dist/foundations/tokenusage/enums/tokenusage.type.js +2 -0
  217. package/dist/foundations/tokenusage/enums/tokenusage.type.js.map +1 -1
  218. package/package.json +1 -1
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.BlogPostService = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const config_1 = require("@nestjs/config");
18
+ const crypto_1 = require("crypto");
19
+ const nestjs_cls_1 = require("nestjs-cls");
20
+ const blocknote_service_1 = require("../../../core/blocknote/services/blocknote.service");
21
+ const jsonapi_paginator_1 = require("../../../core/jsonapi/serialisers/jsonapi.paginator");
22
+ const jsonapi_service_1 = require("../../../core/jsonapi/services/jsonapi.service");
23
+ const abstract_service_1 = require("../../../core/neo4j/abstracts/abstract.service");
24
+ const blog_post_1 = require("../entities/blog-post");
25
+ const blog_post_translation_repository_1 = require("../repositories/blog-post-translation.repository");
26
+ const blog_post_repository_1 = require("../repositories/blog-post.repository");
27
+ const reading_time_1 = require("../utils/reading-time");
28
+ const slug_1 = require("../utils/slug");
29
+ const blog_queue_service_1 = require("./blog-queue.service");
30
+ const s3_client_key_service_1 = require("../../s3/services/s3-client-key.service");
31
+ /** Largest page the public list serves (the web asks for 12 per "Load more" step). */
32
+ const PUBLIC_MAX_PAGE_SIZE = 120;
33
+ const RELATED_LIMIT = 3;
34
+ /**
35
+ * Blog posts: admin writes (original language only), publishing, and the
36
+ * public reads that resolve one language per post.
37
+ */
38
+ let BlogPostService = class BlogPostService extends abstract_service_1.AbstractService {
39
+ constructor(jsonApiService, blogPostRepository, clsService, blogPostTranslationRepository, blogQueueService, blockNoteService, configService, s3ClientKeys) {
40
+ super(jsonApiService, blogPostRepository, clsService, blog_post_1.BlogPostDescriptor.model);
41
+ this.blogPostRepository = blogPostRepository;
42
+ this.blogPostTranslationRepository = blogPostTranslationRepository;
43
+ this.blogQueueService = blogQueueService;
44
+ this.blockNoteService = blockNoteService;
45
+ this.configService = configService;
46
+ this.s3ClientKeys = s3ClientKeys;
47
+ this.descriptor = blog_post_1.BlogPostDescriptor;
48
+ }
49
+ /**
50
+ * Creates a draft with its original-language text, then queues the summary
51
+ * (unless the admin wrote one) and the translation into every other language.
52
+ */
53
+ async createFromDTO(params) {
54
+ // The controller validated the body against BlogPostPostDTO; the crud handler widens it to JsonApiDTOData.
55
+ const data = params.data;
56
+ const attributes = data.attributes;
57
+ // A client-sent image key is checked against the app's S3 key policy before it is stored and signed.
58
+ await this.s3ClientKeys?.assertClientKey({ key: attributes.coverImage });
59
+ const originalLanguage = attributes.originalLanguage;
60
+ const supportedLanguages = this._supportedLanguages();
61
+ if (!supportedLanguages.includes(originalLanguage))
62
+ throw new common_1.BadRequestException(`Unsupported language: ${originalLanguage}`);
63
+ const bodyText = this._plainText(attributes.body);
64
+ const slug = await this._uniqueSlug({ language: originalLanguage, text: attributes.title });
65
+ const summary = attributes.summary?.trim() ? attributes.summary : undefined;
66
+ await this.blogPostRepository.createWithOriginal({
67
+ id: data.id,
68
+ authorId: this.clsService.get("userId"),
69
+ originalLanguage,
70
+ coverImage: attributes.coverImage,
71
+ categoryIds: this._categoryIds(data),
72
+ translation: {
73
+ id: (0, crypto_1.randomUUID)(),
74
+ title: attributes.title,
75
+ slug,
76
+ summary,
77
+ body: attributes.body,
78
+ bodyText,
79
+ coverImageAlt: attributes.coverImageAlt,
80
+ readingMinutes: (0, reading_time_1.readingMinutes)(bodyText),
81
+ },
82
+ });
83
+ await this._queue({
84
+ postId: data.id,
85
+ generateSummary: !summary,
86
+ languages: supportedLanguages.filter((language) => language !== originalLanguage),
87
+ });
88
+ return this.findById({ id: data.id });
89
+ }
90
+ /**
91
+ * Updates the shared fields and the original-language text.
92
+ *
93
+ * - A published post keeps its slugs when the title changes (old links keep
94
+ * working); an explicit `slug` is always applied.
95
+ * - Translation re-runs only when the original's text changed, and it
96
+ * overwrites hand-edited translations.
97
+ * - The summary is regenerated when the body changed and the admin did not
98
+ * edit the summary on this save, or when it was cleared.
99
+ */
100
+ async putFromDTO(params) {
101
+ // The controller validated the body against BlogPostPutDTO; the crud handler widens it to JsonApiDTOData.
102
+ const data = params.data;
103
+ const id = data.id;
104
+ const attributes = data.attributes;
105
+ // A client-sent image key is checked against the app's S3 key policy before it is stored and signed.
106
+ await this.s3ClientKeys?.assertClientKey({ key: attributes.coverImage });
107
+ const post = await this.blogPostRepository.findById({ id });
108
+ if (!post)
109
+ throw new common_1.NotFoundException(`Blog post ${id} not found`);
110
+ const original = await this.blogPostTranslationRepository.findOriginal({ postId: id });
111
+ if (!original)
112
+ throw new common_1.NotFoundException(`Blog post ${id} has no original text`);
113
+ const title = attributes.title;
114
+ const body = attributes.body;
115
+ // Omitted summary = untouched (keep the stored one); an empty string = cleared (regenerate).
116
+ const summary = attributes.summary === undefined ? original.summary : attributes.summary.trim() ? attributes.summary : undefined;
117
+ const coverImageAlt = attributes.coverImageAlt;
118
+ const bodyChanged = body !== original.body;
119
+ const summaryChanged = (summary ?? "") !== (original.summary ?? "");
120
+ const textChanged = bodyChanged ||
121
+ summaryChanged ||
122
+ title !== original.title ||
123
+ (coverImageAlt ?? "") !== (original.coverImageAlt ?? "");
124
+ let slug = original.slug;
125
+ if (attributes.slug?.trim()) {
126
+ slug = await this._uniqueSlug({
127
+ language: post.originalLanguage,
128
+ text: attributes.slug,
129
+ excludeTranslationId: original.id,
130
+ });
131
+ }
132
+ else if (!slug || (post.status !== "published" && title !== original.title)) {
133
+ slug = await this._uniqueSlug({
134
+ language: post.originalLanguage,
135
+ text: title,
136
+ excludeTranslationId: original.id,
137
+ });
138
+ }
139
+ const bodyText = this._plainText(body);
140
+ await this.blogPostRepository.updateShared({
141
+ id,
142
+ // Omitted cover = unchanged: the client only sends a key after a new upload.
143
+ coverImage: attributes.coverImage ?? post.coverImage,
144
+ categoryIds: this._categoryIds(data),
145
+ });
146
+ await this.blogPostTranslationRepository.updateText({
147
+ id: original.id,
148
+ title,
149
+ slug,
150
+ summary,
151
+ body,
152
+ bodyText,
153
+ coverImageAlt,
154
+ readingMinutes: (0, reading_time_1.readingMinutes)(bodyText),
155
+ });
156
+ if (textChanged) {
157
+ await this._queue({
158
+ postId: id,
159
+ generateSummary: !summary || (bodyChanged && !summaryChanged),
160
+ languages: this._supportedLanguages().filter((language) => language !== post.originalLanguage),
161
+ });
162
+ }
163
+ return this.findById({ id });
164
+ }
165
+ /** Deletes the post with every translation. */
166
+ async delete(params) {
167
+ const post = await this.blogPostRepository.findById({ id: params.id });
168
+ if (!post)
169
+ throw new common_1.NotFoundException(`Blog post ${params.id} not found`);
170
+ await this.blogPostRepository.deleteWithTranslations({ id: params.id });
171
+ }
172
+ /** Publishes (or republishes) a post. The first publish date is kept. */
173
+ async publish(params) {
174
+ const post = await this.blogPostRepository.findById({ id: params.id });
175
+ if (!post)
176
+ throw new common_1.NotFoundException(`Blog post ${params.id} not found`);
177
+ await this.blogPostRepository.setStatus({ id: params.id, status: "published" });
178
+ return this.findById({ id: params.id });
179
+ }
180
+ async unpublish(params) {
181
+ const post = await this.blogPostRepository.findById({ id: params.id });
182
+ if (!post)
183
+ throw new common_1.NotFoundException(`Blog post ${params.id} not found`);
184
+ if (post.status !== "published")
185
+ throw new common_1.BadRequestException(`Blog post ${params.id} is not published`);
186
+ await this.blogPostRepository.setStatus({ id: params.id, status: "unpublished" });
187
+ return this.findById({ id: params.id });
188
+ }
189
+ async findPublishedList(params) {
190
+ const paginator = new jsonapi_paginator_1.JsonApiPaginator(this._clampPublicPageSize(params.query));
191
+ const data = await this.blogPostRepository.findPublished({
192
+ language: params.language,
193
+ categorySlug: params.categorySlug || undefined,
194
+ term: params.search?.trim() || undefined,
195
+ cursor: paginator.generateCursor(),
196
+ });
197
+ return this.jsonApiService.buildList(blog_post_1.BlogPostDescriptor.model, data, paginator);
198
+ }
199
+ /** 404 for an unknown slug and for a draft or unpublished post. */
200
+ async findPublishedBySlug(params) {
201
+ const post = await this.blogPostRepository.findPublishedBySlug(params);
202
+ if (!post)
203
+ throw new common_1.NotFoundException(`Blog post ${params.slug} not found`);
204
+ return this.jsonApiService.buildSingle(blog_post_1.BlogPostDescriptor.model, post);
205
+ }
206
+ async findPublishedRelated(params) {
207
+ const post = await this.blogPostRepository.findPublishedBySlug(params);
208
+ if (!post)
209
+ throw new common_1.NotFoundException(`Blog post ${params.slug} not found`);
210
+ const data = await this.blogPostRepository.findPublishedRelated({
211
+ postId: post.id,
212
+ language: params.language,
213
+ limit: RELATED_LIMIT,
214
+ });
215
+ const paginator = new jsonapi_paginator_1.JsonApiPaginator();
216
+ return this.jsonApiService.buildList(blog_post_1.BlogPostDescriptor.model, data, paginator);
217
+ }
218
+ async _queue(params) {
219
+ if (!params.generateSummary && params.languages.length === 0)
220
+ return;
221
+ await this.blogQueueService.enqueuePost(params);
222
+ }
223
+ _supportedLanguages() {
224
+ return this.configService.get("i18n")?.supportedLanguages ?? ["en"];
225
+ }
226
+ _plainText(body) {
227
+ try {
228
+ return this.blockNoteService.convertToPlainText({ nodes: JSON.parse(body) });
229
+ }
230
+ catch {
231
+ throw new common_1.BadRequestException("The body is not valid BlockNote JSON");
232
+ }
233
+ }
234
+ _categoryIds(data) {
235
+ const items = data.relationships?.categories?.data;
236
+ return Array.isArray(items) ? items.map((item) => item.id) : [];
237
+ }
238
+ async _uniqueSlug(params) {
239
+ const base = (0, slug_1.slugify)(params.text);
240
+ const taken = await this.blogPostTranslationRepository.findSlugsWithPrefix({
241
+ language: params.language,
242
+ prefix: base,
243
+ excludeTranslationId: params.excludeTranslationId,
244
+ });
245
+ return (0, slug_1.uniqueSlug)(base, taken);
246
+ }
247
+ /**
248
+ * Anonymous visitors set page[size] through the URL ("Load more" grows it), so the
249
+ * public list caps it: an unbounded size would let anyone force a full-table read.
250
+ */
251
+ _clampPublicPageSize(query) {
252
+ if (!query)
253
+ return query;
254
+ const clamp = (value) => String(Math.min(Math.max(1, Number(value) || 1), PUBLIC_MAX_PAGE_SIZE));
255
+ const clamped = { ...query };
256
+ if (clamped["page[size]"] !== undefined)
257
+ clamped["page[size]"] = clamp(clamped["page[size]"]);
258
+ if (clamped.page?.size !== undefined)
259
+ clamped.page = { ...clamped.page, size: clamp(clamped.page.size) };
260
+ return clamped;
261
+ }
262
+ };
263
+ exports.BlogPostService = BlogPostService;
264
+ exports.BlogPostService = BlogPostService = __decorate([
265
+ (0, common_1.Injectable)(),
266
+ __param(7, (0, common_1.Optional)()),
267
+ __metadata("design:paramtypes", [jsonapi_service_1.JsonApiService,
268
+ blog_post_repository_1.BlogPostRepository,
269
+ nestjs_cls_1.ClsService,
270
+ blog_post_translation_repository_1.BlogPostTranslationRepository,
271
+ blog_queue_service_1.BlogQueueService,
272
+ blocknote_service_1.BlockNoteService,
273
+ config_1.ConfigService,
274
+ s3_client_key_service_1.S3ClientKeyService])
275
+ ], BlogPostService);
276
+ //# sourceMappingURL=blog-post.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-post.service.js","sourceRoot":"","sources":["../../../../src/foundations/blog/services/blog-post.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { Queue } from "bullmq";
2
+ import { BlogCategoryTranslationRepository } from "../repositories/blog-category-translation.repository";
3
+ import { BlogPostTranslationRepository } from "../repositories/blog-post-translation.repository";
4
+ /**
5
+ * Enqueues the blog's AI jobs (summary + translation).
6
+ *
7
+ * Every enqueue first creates (or keeps) one placeholder translation per target
8
+ * language in `pending`, so the admin sees "Translating…" at once.
9
+ *
10
+ * The payloads never carry `companyId`: the blog is platform content, so token
11
+ * usage is recorded against the post or category and no company is charged.
12
+ */
13
+ export declare class BlogQueueService {
14
+ private readonly queue;
15
+ private readonly blogPostTranslationRepository;
16
+ private readonly blogCategoryTranslationRepository;
17
+ constructor(queue: Queue, blogPostTranslationRepository: BlogPostTranslationRepository, blogCategoryTranslationRepository: BlogCategoryTranslationRepository);
18
+ enqueuePost(params: {
19
+ postId: string;
20
+ generateSummary: boolean;
21
+ languages: string[];
22
+ }): Promise<void>;
23
+ enqueuePostLanguage(params: {
24
+ postId: string;
25
+ language: string;
26
+ }): Promise<void>;
27
+ enqueueCategory(params: {
28
+ categoryId: string;
29
+ languages: string[];
30
+ }): Promise<void>;
31
+ enqueueCategoryLanguage(params: {
32
+ categoryId: string;
33
+ language: string;
34
+ }): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=blog-queue.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-queue.service.d.ts","sourceRoot":"","sources":["../../../../src/foundations/blog/services/blog-queue.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAG/B,OAAO,EAAE,iCAAiC,EAAE,MAAM,sDAAsD,CAAC;AACzG,OAAO,EAAE,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAEjG;;;;;;;;GAQG;AACH,qBACa,gBAAgB;IAEE,OAAO,CAAC,QAAQ,CAAC,KAAK;IACjD,OAAO,CAAC,QAAQ,CAAC,6BAA6B;IAC9C,OAAO,CAAC,QAAQ,CAAC,iCAAiC;gBAFN,KAAK,EAAE,KAAK,EACvC,6BAA6B,EAAE,6BAA6B,EAC5D,iCAAiC,EAAE,iCAAiC;IAGjF,WAAW,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IASrG,mBAAmB,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhF,eAAe,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAWnF,uBAAuB,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAU/F"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.BlogQueueService = void 0;
16
+ const bullmq_1 = require("@nestjs/bullmq");
17
+ const common_1 = require("@nestjs/common");
18
+ const bullmq_2 = require("bullmq");
19
+ const queue_id_1 = require("../../../config/enums/queue.id");
20
+ const blog_constants_1 = require("../blog.constants");
21
+ const blog_category_translation_repository_1 = require("../repositories/blog-category-translation.repository");
22
+ const blog_post_translation_repository_1 = require("../repositories/blog-post-translation.repository");
23
+ /**
24
+ * Enqueues the blog's AI jobs (summary + translation).
25
+ *
26
+ * Every enqueue first creates (or keeps) one placeholder translation per target
27
+ * language in `pending`, so the admin sees "Translating…" at once.
28
+ *
29
+ * The payloads never carry `companyId`: the blog is platform content, so token
30
+ * usage is recorded against the post or category and no company is charged.
31
+ */
32
+ let BlogQueueService = class BlogQueueService {
33
+ constructor(queue, blogPostTranslationRepository, blogCategoryTranslationRepository) {
34
+ this.queue = queue;
35
+ this.blogPostTranslationRepository = blogPostTranslationRepository;
36
+ this.blogCategoryTranslationRepository = blogCategoryTranslationRepository;
37
+ }
38
+ async enqueuePost(params) {
39
+ await this.blogPostTranslationRepository.ensurePending({ postId: params.postId, languages: params.languages });
40
+ await this.queue.add(blog_constants_1.BlogJobName.ProcessPost, {
41
+ postId: params.postId,
42
+ generateSummary: params.generateSummary,
43
+ languages: params.languages,
44
+ });
45
+ }
46
+ async enqueuePostLanguage(params) {
47
+ await this.blogPostTranslationRepository.ensurePending({ postId: params.postId, languages: [params.language] });
48
+ await this.queue.add(blog_constants_1.BlogJobName.TranslatePost, { postId: params.postId, language: params.language });
49
+ }
50
+ async enqueueCategory(params) {
51
+ await this.blogCategoryTranslationRepository.ensurePending({
52
+ categoryId: params.categoryId,
53
+ languages: params.languages,
54
+ });
55
+ await this.queue.add(blog_constants_1.BlogJobName.ProcessCategory, {
56
+ categoryId: params.categoryId,
57
+ languages: params.languages,
58
+ });
59
+ }
60
+ async enqueueCategoryLanguage(params) {
61
+ await this.blogCategoryTranslationRepository.ensurePending({
62
+ categoryId: params.categoryId,
63
+ languages: [params.language],
64
+ });
65
+ await this.queue.add(blog_constants_1.BlogJobName.TranslateCategory, {
66
+ categoryId: params.categoryId,
67
+ language: params.language,
68
+ });
69
+ }
70
+ };
71
+ exports.BlogQueueService = BlogQueueService;
72
+ exports.BlogQueueService = BlogQueueService = __decorate([
73
+ (0, common_1.Injectable)(),
74
+ __param(0, (0, bullmq_1.InjectQueue)(queue_id_1.QueueId.BLOG)),
75
+ __metadata("design:paramtypes", [bullmq_2.Queue,
76
+ blog_post_translation_repository_1.BlogPostTranslationRepository,
77
+ blog_category_translation_repository_1.BlogCategoryTranslationRepository])
78
+ ], BlogQueueService);
79
+ //# sourceMappingURL=blog-queue.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-queue.service.js","sourceRoot":"","sources":["../../../../src/foundations/blog/services/blog-queue.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA6C;AAC7C,2CAA4C;AAC5C,mCAA+B;AAC/B,6DAAyD;AACzD,sDAAgD;AAChD,+GAAyG;AACzG,uGAAiG;AAEjG;;;;;;;;GAQG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAC3B,YAC8C,KAAY,EACvC,6BAA4D,EAC5D,iCAAoE;QAFzC,UAAK,GAAL,KAAK,CAAO;QACvC,kCAA6B,GAA7B,6BAA6B,CAA+B;QAC5D,sCAAiC,GAAjC,iCAAiC,CAAmC;IACpF,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,MAAyE;QACzF,MAAM,IAAI,CAAC,6BAA6B,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/G,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,4BAAW,CAAC,WAAW,EAAE;YAC5C,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,MAA4C;QACpE,MAAM,IAAI,CAAC,6BAA6B,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,4BAAW,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxG,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAmD;QACvE,MAAM,IAAI,CAAC,iCAAiC,CAAC,aAAa,CAAC;YACzD,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,4BAAW,CAAC,eAAe,EAAE;YAChD,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,MAAgD;QAC5E,MAAM,IAAI,CAAC,iCAAiC,CAAC,aAAa,CAAC;YACzD,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,SAAS,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC;SAC7B,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,4BAAW,CAAC,iBAAiB,EAAE;YAClD,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ;SAC1B,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AA1CY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;IAGR,WAAA,IAAA,oBAAW,EAAC,kBAAO,CAAC,IAAI,CAAC,CAAA;qCAAyB,cAAK;QACR,gEAA6B;QACzB,wEAAiC;GAJ5E,gBAAgB,CA0C5B"}
@@ -0,0 +1,21 @@
1
+ import { ClsService } from "nestjs-cls";
2
+ import { JsonApiSingleDocument } from "../../../core/jsonapi/interfaces/jsonapi.document.interface";
3
+ import { JsonApiService } from "../../../core/jsonapi/services/jsonapi.service";
4
+ import { AbstractService } from "../../../core/neo4j/abstracts/abstract.service";
5
+ import { BlogSettingsPutDTO } from "../dtos/blog-settings.put.dto";
6
+ import { BlogSettings, BlogSettingsDescriptor } from "../entities/blog-settings";
7
+ import { BlogSettingsRepository } from "../repositories/blog-settings.repository";
8
+ /**
9
+ * The blog's singleton settings (the public list layout). Until an admin saves
10
+ * them, reads return the default `featured` layout under BLOG_SETTINGS_ID.
11
+ */
12
+ export declare class BlogSettingsService extends AbstractService<BlogSettings, typeof BlogSettingsDescriptor.relationships> {
13
+ private readonly blogSettingsRepository;
14
+ protected readonly descriptor: import("../../..").EntityDescriptor<BlogSettings, {}>;
15
+ constructor(jsonApiService: JsonApiService, blogSettingsRepository: BlogSettingsRepository, clsService: ClsService);
16
+ getSettings(): Promise<JsonApiSingleDocument>;
17
+ saveSettings(params: {
18
+ data: BlogSettingsPutDTO["data"];
19
+ }): Promise<JsonApiSingleDocument>;
20
+ }
21
+ //# sourceMappingURL=blog-settings.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-settings.service.d.ts","sourceRoot":"","sources":["../../../../src/foundations/blog/services/blog-settings.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,6DAA6D,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,gDAAgD,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AAElF;;;GAGG;AACH,qBACa,mBAAoB,SAAQ,eAAe,CAAC,YAAY,EAAE,OAAO,sBAAsB,CAAC,aAAa,CAAC;IAK/G,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IAJzC,SAAS,CAAC,QAAQ,CAAC,UAAU,wDAA0B;gBAGrD,cAAc,EAAE,cAAc,EACb,sBAAsB,EAAE,sBAAsB,EAC/D,UAAU,EAAE,UAAU;IAKlB,WAAW,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAM7C,YAAY,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAKjG"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.BlogSettingsService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const nestjs_cls_1 = require("nestjs-cls");
15
+ const jsonapi_service_1 = require("../../../core/jsonapi/services/jsonapi.service");
16
+ const abstract_service_1 = require("../../../core/neo4j/abstracts/abstract.service");
17
+ const blog_constants_1 = require("../blog.constants");
18
+ const blog_settings_1 = require("../entities/blog-settings");
19
+ const blog_settings_repository_1 = require("../repositories/blog-settings.repository");
20
+ /**
21
+ * The blog's singleton settings (the public list layout). Until an admin saves
22
+ * them, reads return the default `featured` layout under BLOG_SETTINGS_ID.
23
+ */
24
+ let BlogSettingsService = class BlogSettingsService extends abstract_service_1.AbstractService {
25
+ constructor(jsonApiService, blogSettingsRepository, clsService) {
26
+ super(jsonApiService, blogSettingsRepository, clsService, blog_settings_1.BlogSettingsDescriptor.model);
27
+ this.blogSettingsRepository = blogSettingsRepository;
28
+ this.descriptor = blog_settings_1.BlogSettingsDescriptor;
29
+ }
30
+ async getSettings() {
31
+ const stored = await this.blogSettingsRepository.findSettings();
32
+ const settings = stored ?? { id: blog_constants_1.BLOG_SETTINGS_ID, listLayout: "featured" };
33
+ return this.jsonApiService.buildSingle(blog_settings_1.BlogSettingsDescriptor.model, settings);
34
+ }
35
+ async saveSettings(params) {
36
+ if (params.data.id !== blog_constants_1.BLOG_SETTINGS_ID)
37
+ throw new common_1.BadRequestException("Unknown blog settings id");
38
+ await this.blogSettingsRepository.upsert({ listLayout: params.data.attributes.listLayout });
39
+ return this.getSettings();
40
+ }
41
+ };
42
+ exports.BlogSettingsService = BlogSettingsService;
43
+ exports.BlogSettingsService = BlogSettingsService = __decorate([
44
+ (0, common_1.Injectable)(),
45
+ __metadata("design:paramtypes", [jsonapi_service_1.JsonApiService,
46
+ blog_settings_repository_1.BlogSettingsRepository,
47
+ nestjs_cls_1.ClsService])
48
+ ], BlogSettingsService);
49
+ //# sourceMappingURL=blog-settings.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-settings.service.js","sourceRoot":"","sources":["../../../../src/foundations/blog/services/blog-settings.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiE;AACjE,2CAAwC;AAExC,oFAAgF;AAChF,qFAAiF;AACjF,sDAAqD;AAErD,6DAAiF;AACjF,uFAAkF;AAElF;;;GAGG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,kCAA0E;IAGjH,YACE,cAA8B,EACb,sBAA8C,EAC/D,UAAsB;QAEtB,KAAK,CAAC,cAAc,EAAE,sBAAsB,EAAE,UAAU,EAAE,sCAAsB,CAAC,KAAK,CAAC,CAAC;QAHvE,2BAAsB,GAAtB,sBAAsB,CAAwB;QAJ9C,eAAU,GAAG,sCAAsB,CAAC;IAQvD,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,YAAY,EAAE,CAAC;QAChE,MAAM,QAAQ,GAAG,MAAM,IAAK,EAAE,EAAE,EAAE,iCAAgB,EAAE,UAAU,EAAE,UAAU,EAAmB,CAAC;QAC9F,OAAO,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,sCAAsB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAA4C;QAC7D,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,iCAAgB;YAAE,MAAM,IAAI,4BAAmB,CAAC,0BAA0B,CAAC,CAAC;QACnG,MAAM,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5F,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;CACF,CAAA;AAtBY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;qCAKO,gCAAc;QACW,iDAAsB;QACnD,uBAAU;GANb,mBAAmB,CAsB/B"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Reading time in whole minutes at 200 words per minute, never less than 1.
3
+ */
4
+ export declare function readingMinutes(plainText: string): number;
5
+ //# sourceMappingURL=reading-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reading-time.d.ts","sourceRoot":"","sources":["../../../../src/foundations/blog/utils/reading-time.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGxD"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readingMinutes = readingMinutes;
4
+ const WORDS_PER_MINUTE = 200;
5
+ /**
6
+ * Reading time in whole minutes at 200 words per minute, never less than 1.
7
+ */
8
+ function readingMinutes(plainText) {
9
+ const words = (plainText ?? "").split(/\s+/).filter((word) => word.length > 0).length;
10
+ return Math.max(1, Math.ceil(words / WORDS_PER_MINUTE));
11
+ }
12
+ //# sourceMappingURL=reading-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reading-time.js","sourceRoot":"","sources":["../../../../src/foundations/blog/utils/reading-time.ts"],"names":[],"mappings":";;AAKA,wCAGC;AARD,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B;;GAEG;AACH,SAAgB,cAAc,CAAC,SAAiB;IAC9C,MAAM,KAAK,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;IACtF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,gBAAgB,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Lowercase kebab-case ASCII slug: diacritics stripped (NFKD), every run of characters outside
3
+ * [a-z0-9] collapsed to one dash, dashes trimmed from both ends, at most 80 characters.
4
+ * Returns "post" when nothing usable is left.
5
+ */
6
+ export declare function slugify(text: string): string;
7
+ /**
8
+ * Returns `base` when it is free, otherwise the first free `base-2`, `base-3`, ...
9
+ * `taken` is the list of slugs already used in the same language.
10
+ */
11
+ export declare function uniqueSlug(base: string, taken: string[]): string;
12
+ //# sourceMappingURL=slug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../../../../src/foundations/blog/utils/slug.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW5C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAOhE"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.slugify = slugify;
4
+ exports.uniqueSlug = uniqueSlug;
5
+ const MAX_SLUG_LENGTH = 80;
6
+ const EMPTY_SLUG = "post";
7
+ /**
8
+ * Lowercase kebab-case ASCII slug: diacritics stripped (NFKD), every run of characters outside
9
+ * [a-z0-9] collapsed to one dash, dashes trimmed from both ends, at most 80 characters.
10
+ * Returns "post" when nothing usable is left.
11
+ */
12
+ function slugify(text) {
13
+ const slug = (text ?? "")
14
+ .normalize("NFKD")
15
+ .replace(/[̀-ͯ]/g, "")
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9]+/g, "-")
18
+ .replace(/^-+|-+$/g, "")
19
+ .slice(0, MAX_SLUG_LENGTH)
20
+ .replace(/-+$/g, "");
21
+ return slug.length > 0 ? slug : EMPTY_SLUG;
22
+ }
23
+ /**
24
+ * Returns `base` when it is free, otherwise the first free `base-2`, `base-3`, ...
25
+ * `taken` is the list of slugs already used in the same language.
26
+ */
27
+ function uniqueSlug(base, taken) {
28
+ const used = new Set(taken);
29
+ if (!used.has(base))
30
+ return base;
31
+ let suffix = 2;
32
+ while (used.has(`${base}-${suffix}`))
33
+ suffix++;
34
+ return `${base}-${suffix}`;
35
+ }
36
+ //# sourceMappingURL=slug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug.js","sourceRoot":"","sources":["../../../../src/foundations/blog/utils/slug.ts"],"names":[],"mappings":";;AAQA,0BAWC;AAMD,gCAOC;AAhCD,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B;;;;GAIG;AACH,SAAgB,OAAO,CAAC,IAAY;IAClC,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;SACtB,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;SACzB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEvB,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,SAAgB,UAAU,CAAC,IAAY,EAAE,KAAe;IACtD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEjC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC;QAAE,MAAM,EAAE,CAAC;IAC/C,OAAO,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC;AAC7B,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { DynamicModule, Type } from "@nestjs/common";
2
2
  import { AnalyticsModuleConfig } from "./analytics/interfaces/analytics.config.interface";
3
+ import { BlogModuleConfig } from "./blog/interfaces/blog.config.interface";
3
4
  import { ContentExtensionConfig } from "./content/interfaces/content.extension.interface";
4
5
  import { ReferralModuleConfig } from "./referral/interfaces/referral.config.interface";
5
6
  import { EmailAutomationModuleConfig } from "./email-automation/interfaces/email-automation.config.interface";
@@ -20,6 +21,13 @@ export interface FoundationsModuleConfig {
20
21
  handbook?: HandbookModuleConfig;
21
22
  /** Optional configuration for the how-to feature module (opt-in public routes) */
22
23
  howTo?: HowToModuleConfig;
24
+ /**
25
+ * The blog feature module. OPT-IN: the blog mounts only when this key is
26
+ * present (`blog: {}` for the admin routes, `blog: { publicRoutes: true }`
27
+ * for the public reads too). Omitted, no blog controller, queue or worker is
28
+ * mounted, so an app with the permission gate on needs no Blog section seed.
29
+ */
30
+ blog?: BlogModuleConfig;
23
31
  /** Optional configuration for the email-automation feature module (the triggers the app declares) */
24
32
  emailAutomation?: EmailAutomationModuleConfig;
25
33
  /** Optional override for the analytics feature module (normally switched on by `ANALYTICS_ENABLED=true`) */
@@ -1 +1 @@
1
- {"version":3,"file":"foundations.modules.d.ts","sourceRoot":"","sources":["../../src/foundations/foundations.modules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAG7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAC;AAS1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kDAAkD,CAAC;AAE1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAGvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,iEAAiE,CAAC;AAG9G,OAAO,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAEvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAyBhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAC;AAGrG;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,kEAAkE;IAClE,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,kFAAkF;IAClF,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,qGAAqG;IACrG,eAAe,CAAC,EAAE,2BAA2B,CAAC;IAC9C,4GAA4G;IAC5G,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;CACvB;AA0DD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,iBAAiB;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,uBAAuB,GAAG,aAAa;CA2BhE"}
1
+ {"version":3,"file":"foundations.modules.d.ts","sourceRoot":"","sources":["../../src/foundations/foundations.modules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAG7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAC;AAM1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAK3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,kDAAkD,CAAC;AAE1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAGvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,iEAAiE,CAAC;AAG9G,OAAO,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAEvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAyBhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAC;AAGrG;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,kEAAkE;IAClE,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,kFAAkF;IAClF,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,qGAAqG;IACrG,eAAe,CAAC,EAAE,2BAA2B,CAAC;IAC9C,4GAA4G;IAC5G,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;CACvB;AA0DD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,iBAAiB;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,uBAAuB,GAAG,aAAa;CA6BhE"}