bozonx-social-media-posting 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1003 -0
  3. package/dist/src/app.constants.d.ts +8 -0
  4. package/dist/src/app.constants.js +9 -0
  5. package/dist/src/app.constants.js.map +1 -0
  6. package/dist/src/common/enums/body-format.enum.d.ts +12 -0
  7. package/dist/src/common/enums/body-format.enum.js +14 -0
  8. package/dist/src/common/enums/body-format.enum.js.map +1 -0
  9. package/dist/src/common/enums/error-code.enum.d.ts +12 -0
  10. package/dist/src/common/enums/error-code.enum.js +14 -0
  11. package/dist/src/common/enums/error-code.enum.js.map +1 -0
  12. package/dist/src/common/enums/index.d.ts +3 -0
  13. package/dist/src/common/enums/index.js +4 -0
  14. package/dist/src/common/enums/index.js.map +1 -0
  15. package/dist/src/common/enums/post-type.enum.d.ts +28 -0
  16. package/dist/src/common/enums/post-type.enum.js +30 -0
  17. package/dist/src/common/enums/post-type.enum.js.map +1 -0
  18. package/dist/src/common/filters/all-exceptions.filter.d.ts +13 -0
  19. package/dist/src/common/filters/all-exceptions.filter.js +103 -0
  20. package/dist/src/common/filters/all-exceptions.filter.js.map +1 -0
  21. package/dist/src/common/helpers/media-input.helper.d.ts +73 -0
  22. package/dist/src/common/helpers/media-input.helper.js +122 -0
  23. package/dist/src/common/helpers/media-input.helper.js.map +1 -0
  24. package/dist/src/common/interceptors/shutdown.interceptor.d.ts +12 -0
  25. package/dist/src/common/interceptors/shutdown.interceptor.js +41 -0
  26. package/dist/src/common/interceptors/shutdown.interceptor.js.map +1 -0
  27. package/dist/src/common/interfaces/logger.interface.d.ts +44 -0
  28. package/dist/src/common/interfaces/logger.interface.js +44 -0
  29. package/dist/src/common/interfaces/logger.interface.js.map +1 -0
  30. package/dist/src/common/services/shutdown.module.d.ts +2 -0
  31. package/dist/src/common/services/shutdown.module.js +18 -0
  32. package/dist/src/common/services/shutdown.module.js.map +1 -0
  33. package/dist/src/common/services/shutdown.service.d.ts +40 -0
  34. package/dist/src/common/services/shutdown.service.js +122 -0
  35. package/dist/src/common/services/shutdown.service.js.map +1 -0
  36. package/dist/src/common/types/index.d.ts +1 -0
  37. package/dist/src/common/types/index.js +2 -0
  38. package/dist/src/common/types/index.js.map +1 -0
  39. package/dist/src/common/types/media-input.type.d.ts +29 -0
  40. package/dist/src/common/types/media-input.type.js +2 -0
  41. package/dist/src/common/types/media-input.type.js.map +1 -0
  42. package/dist/src/common/validators/body-length.validator.d.ts +24 -0
  43. package/dist/src/common/validators/body-length.validator.js +57 -0
  44. package/dist/src/common/validators/body-length.validator.js.map +1 -0
  45. package/dist/src/common/validators/channel-id.validator.d.ts +19 -0
  46. package/dist/src/common/validators/channel-id.validator.js +58 -0
  47. package/dist/src/common/validators/channel-id.validator.js.map +1 -0
  48. package/dist/src/common/validators/has-content.validator.d.ts +23 -0
  49. package/dist/src/common/validators/has-content.validator.js +57 -0
  50. package/dist/src/common/validators/has-content.validator.js.map +1 -0
  51. package/dist/src/common/validators/media-input.validator.d.ts +44 -0
  52. package/dist/src/common/validators/media-input.validator.js +112 -0
  53. package/dist/src/common/validators/media-input.validator.js.map +1 -0
  54. package/dist/src/common/validators/media-priority.validator.d.ts +19 -0
  55. package/dist/src/common/validators/media-priority.validator.js +38 -0
  56. package/dist/src/common/validators/media-priority.validator.js.map +1 -0
  57. package/dist/src/config/app.config.d.ts +33 -0
  58. package/dist/src/config/app.config.js +83 -0
  59. package/dist/src/config/app.config.js.map +1 -0
  60. package/dist/src/config/library.config.d.ts +51 -0
  61. package/dist/src/config/library.config.js +197 -0
  62. package/dist/src/config/library.config.js.map +1 -0
  63. package/dist/src/config/yaml-config.dto.d.ts +37 -0
  64. package/dist/src/config/yaml-config.dto.js +152 -0
  65. package/dist/src/config/yaml-config.dto.js.map +1 -0
  66. package/dist/src/config/yaml.config.d.ts +14 -0
  67. package/dist/src/config/yaml.config.js +72 -0
  68. package/dist/src/config/yaml.config.js.map +1 -0
  69. package/dist/src/index.d.ts +19 -0
  70. package/dist/src/index.js +17 -0
  71. package/dist/src/index.js.map +1 -0
  72. package/dist/src/library.d.ts +57 -0
  73. package/dist/src/library.js +92 -0
  74. package/dist/src/library.js.map +1 -0
  75. package/dist/src/modules/app-config/app-config.module.d.ts +2 -0
  76. package/dist/src/modules/app-config/app-config.module.js +26 -0
  77. package/dist/src/modules/app-config/app-config.module.js.map +1 -0
  78. package/dist/src/modules/app-config/app-config.service.d.ts +14 -0
  79. package/dist/src/modules/app-config/app-config.service.js +18 -0
  80. package/dist/src/modules/app-config/app-config.service.js.map +1 -0
  81. package/dist/src/modules/app-config/interfaces/app-config.interface.d.ts +31 -0
  82. package/dist/src/modules/app-config/interfaces/app-config.interface.js +2 -0
  83. package/dist/src/modules/app-config/interfaces/app-config.interface.js.map +1 -0
  84. package/dist/src/modules/app-config/nest-config.service.d.ts +41 -0
  85. package/dist/src/modules/app-config/nest-config.service.js +91 -0
  86. package/dist/src/modules/app-config/nest-config.service.js.map +1 -0
  87. package/dist/src/modules/health/health.controller.d.ts +12 -0
  88. package/dist/src/modules/health/health.controller.js +33 -0
  89. package/dist/src/modules/health/health.controller.js.map +1 -0
  90. package/dist/src/modules/health/health.module.d.ts +2 -0
  91. package/dist/src/modules/health/health.module.js +17 -0
  92. package/dist/src/modules/health/health.module.js.map +1 -0
  93. package/dist/src/modules/media/media.module.d.ts +2 -0
  94. package/dist/src/modules/media/media.module.js +18 -0
  95. package/dist/src/modules/media/media.module.js.map +1 -0
  96. package/dist/src/modules/media/media.service.d.ts +15 -0
  97. package/dist/src/modules/media/media.service.js +49 -0
  98. package/dist/src/modules/media/media.service.js.map +1 -0
  99. package/dist/src/modules/platforms/base/auth-validator-registry.service.d.ts +25 -0
  100. package/dist/src/modules/platforms/base/auth-validator-registry.service.js +50 -0
  101. package/dist/src/modules/platforms/base/auth-validator-registry.service.js.map +1 -0
  102. package/dist/src/modules/platforms/base/auth-validator.interface.d.ts +16 -0
  103. package/dist/src/modules/platforms/base/auth-validator.interface.js +2 -0
  104. package/dist/src/modules/platforms/base/auth-validator.interface.js.map +1 -0
  105. package/dist/src/modules/platforms/base/index.d.ts +4 -0
  106. package/dist/src/modules/platforms/base/index.js +5 -0
  107. package/dist/src/modules/platforms/base/index.js.map +1 -0
  108. package/dist/src/modules/platforms/base/platform-registry.service.d.ts +31 -0
  109. package/dist/src/modules/platforms/base/platform-registry.service.js +54 -0
  110. package/dist/src/modules/platforms/base/platform-registry.service.js.map +1 -0
  111. package/dist/src/modules/platforms/base/platform.interface.d.ts +39 -0
  112. package/dist/src/modules/platforms/base/platform.interface.js +2 -0
  113. package/dist/src/modules/platforms/base/platform.interface.js.map +1 -0
  114. package/dist/src/modules/platforms/platforms.module.d.ts +13 -0
  115. package/dist/src/modules/platforms/platforms.module.js +59 -0
  116. package/dist/src/modules/platforms/platforms.module.js.map +1 -0
  117. package/dist/src/modules/platforms/telegram/telegram-auth.validator.d.ts +19 -0
  118. package/dist/src/modules/platforms/telegram/telegram-auth.validator.js +51 -0
  119. package/dist/src/modules/platforms/telegram/telegram-auth.validator.js.map +1 -0
  120. package/dist/src/modules/platforms/telegram/telegram-type-detector.service.d.ts +18 -0
  121. package/dist/src/modules/platforms/telegram/telegram-type-detector.service.js +47 -0
  122. package/dist/src/modules/platforms/telegram/telegram-type-detector.service.js.map +1 -0
  123. package/dist/src/modules/platforms/telegram/telegram.platform.d.ts +58 -0
  124. package/dist/src/modules/platforms/telegram/telegram.platform.js +434 -0
  125. package/dist/src/modules/platforms/telegram/telegram.platform.js.map +1 -0
  126. package/dist/src/modules/post/base-post.service.d.ts +64 -0
  127. package/dist/src/modules/post/base-post.service.js +99 -0
  128. package/dist/src/modules/post/base-post.service.js.map +1 -0
  129. package/dist/src/modules/post/dto/index.d.ts +3 -0
  130. package/dist/src/modules/post/dto/index.js +4 -0
  131. package/dist/src/modules/post/dto/index.js.map +1 -0
  132. package/dist/src/modules/post/dto/post-request.dto.d.ts +56 -0
  133. package/dist/src/modules/post/dto/post-request.dto.js +195 -0
  134. package/dist/src/modules/post/dto/post-request.dto.js.map +1 -0
  135. package/dist/src/modules/post/dto/post-response.dto.d.ts +41 -0
  136. package/dist/src/modules/post/dto/post-response.dto.js +2 -0
  137. package/dist/src/modules/post/dto/post-response.dto.js.map +1 -0
  138. package/dist/src/modules/post/dto/preview-response.dto.d.ts +33 -0
  139. package/dist/src/modules/post/dto/preview-response.dto.js +2 -0
  140. package/dist/src/modules/post/dto/preview-response.dto.js.map +1 -0
  141. package/dist/src/modules/post/idempotency.service.d.ts +95 -0
  142. package/dist/src/modules/post/idempotency.service.js +229 -0
  143. package/dist/src/modules/post/idempotency.service.js.map +1 -0
  144. package/dist/src/modules/post/post.controller.d.ts +13 -0
  145. package/dist/src/modules/post/post.controller.js +97 -0
  146. package/dist/src/modules/post/post.controller.js.map +1 -0
  147. package/dist/src/modules/post/post.module.d.ts +2 -0
  148. package/dist/src/modules/post/post.module.js +25 -0
  149. package/dist/src/modules/post/post.module.js.map +1 -0
  150. package/dist/src/modules/post/post.service.d.ts +62 -0
  151. package/dist/src/modules/post/post.service.js +325 -0
  152. package/dist/src/modules/post/post.service.js.map +1 -0
  153. package/dist/src/modules/post/preview.service.d.ts +23 -0
  154. package/dist/src/modules/post/preview.service.js +69 -0
  155. package/dist/src/modules/post/preview.service.js.map +1 -0
  156. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  157. package/package.json +102 -0
@@ -0,0 +1,56 @@
1
+ import { PostType } from '../../../common/enums/index.js';
2
+ import type { MediaInput } from '../../../common/types/index.js';
3
+ /**
4
+ * Post request DTO
5
+ * Contains all data needed to publish a post to a social media platform
6
+ */
7
+ export declare class PostRequestDto {
8
+ /** Target social media platform (e.g., 'telegram') */
9
+ platform: string;
10
+ /** Post content/text body (optional if media is provided) */
11
+ body?: string;
12
+ /** Post type (auto-detected if not specified) */
13
+ type?: PostType;
14
+ /**
15
+ * Format of the body content.
16
+ * Standard values: 'text', 'html', 'md'
17
+ * Platform-specific values (e.g., 'MarkdownV2' for Telegram) are also supported
18
+ */
19
+ bodyFormat?: string;
20
+ /** Post title (used by platforms that support it, max 1000 characters) */
21
+ title?: string;
22
+ /** Post description/summary (used by platforms that support it, max 5000 characters) */
23
+ description?: string;
24
+ /** Cover image (for image posts or article thumbnails) */
25
+ cover?: MediaInput;
26
+ /** Video file (for video posts) */
27
+ video?: MediaInput;
28
+ /** Audio file (for audio posts) */
29
+ audio?: MediaInput;
30
+ /** Document file (for document posts) */
31
+ document?: MediaInput;
32
+ /** Multiple media files (for album/gallery posts) */
33
+ media?: MediaInput[];
34
+ /** Named account from configuration */
35
+ account?: string;
36
+ /** Platform-agnostic channel/chat identifier (e.g., @mychannel, -100123456789, or 123456789) */
37
+ channelId?: string | number;
38
+ /** Inline authentication credentials (alternative to account) */
39
+ auth?: Record<string, any>;
40
+ /** Platform-specific options */
41
+ options?: Record<string, any>;
42
+ /** Disable notification (silent message) */
43
+ disableNotification?: boolean;
44
+ /** Post tags/hashtags (max 200 items, each max 300 characters) */
45
+ tags?: string[];
46
+ /** Scheduled publication time (ISO 8601 format, max 50 characters) */
47
+ scheduledAt?: string;
48
+ /** Post language code (e.g., 'en', 'ru', max 50 characters) */
49
+ postLanguage?: string;
50
+ /** Publication mode: publish immediately or save as draft */
51
+ mode?: 'publish' | 'draft';
52
+ /** Idempotency key to prevent duplicate posts (max 1000 characters) */
53
+ idempotencyKey?: string;
54
+ /** Maximum body length override (max 500,000 characters) */
55
+ maxBody?: number;
56
+ }
@@ -0,0 +1,195 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { ArrayMaxSize, IsArray, IsBoolean, IsEnum, IsNumber, IsObject, IsOptional, IsString, Max, MaxLength, Min, } from 'class-validator';
11
+ import { PostType } from '../../../common/enums/index.js';
12
+ import { IsMediaInput, IsMediaInputArray, } from '../../../common/validators/media-input.validator.js';
13
+ import { IsValidBodyLength, MAX_BODY_LIMIT, } from '../../../common/validators/body-length.validator.js';
14
+ import { IsChannelId } from '../../../common/validators/channel-id.validator.js';
15
+ import { HasContent } from '../../../common/validators/has-content.validator.js';
16
+ /**
17
+ * Post request DTO
18
+ * Contains all data needed to publish a post to a social media platform
19
+ */
20
+ let PostRequestDto = class PostRequestDto {
21
+ /** Target social media platform (e.g., 'telegram') */
22
+ platform;
23
+ /** Post content/text body (optional if media is provided) */
24
+ body;
25
+ /** Post type (auto-detected if not specified) */
26
+ type = PostType.AUTO;
27
+ /**
28
+ * Format of the body content.
29
+ * Standard values: 'text', 'html', 'md'
30
+ * Platform-specific values (e.g., 'MarkdownV2' for Telegram) are also supported
31
+ */
32
+ bodyFormat = 'text';
33
+ /** Post title (used by platforms that support it, max 1000 characters) */
34
+ title;
35
+ /** Post description/summary (used by platforms that support it, max 5000 characters) */
36
+ description;
37
+ /** Cover image (for image posts or article thumbnails) */
38
+ cover;
39
+ /** Video file (for video posts) */
40
+ video;
41
+ /** Audio file (for audio posts) */
42
+ audio;
43
+ /** Document file (for document posts) */
44
+ document;
45
+ /** Multiple media files (for album/gallery posts) */
46
+ media;
47
+ /** Named account from configuration */
48
+ account;
49
+ /** Platform-agnostic channel/chat identifier (e.g., @mychannel, -100123456789, or 123456789) */
50
+ channelId;
51
+ /** Inline authentication credentials (alternative to account) */
52
+ auth;
53
+ /** Platform-specific options */
54
+ options;
55
+ /** Disable notification (silent message) */
56
+ disableNotification;
57
+ /** Post tags/hashtags (max 200 items, each max 300 characters) */
58
+ tags;
59
+ /** Scheduled publication time (ISO 8601 format, max 50 characters) */
60
+ scheduledAt;
61
+ /** Post language code (e.g., 'en', 'ru', max 50 characters) */
62
+ postLanguage;
63
+ /** Publication mode: publish immediately or save as draft */
64
+ mode;
65
+ /** Idempotency key to prevent duplicate posts (max 1000 characters) */
66
+ idempotencyKey;
67
+ /** Maximum body length override (max 500,000 characters) */
68
+ maxBody;
69
+ };
70
+ __decorate([
71
+ IsString(),
72
+ __metadata("design:type", String)
73
+ ], PostRequestDto.prototype, "platform", void 0);
74
+ __decorate([
75
+ IsOptional(),
76
+ IsString(),
77
+ IsValidBodyLength(),
78
+ __metadata("design:type", String)
79
+ ], PostRequestDto.prototype, "body", void 0);
80
+ __decorate([
81
+ IsOptional(),
82
+ IsEnum(PostType),
83
+ __metadata("design:type", String)
84
+ ], PostRequestDto.prototype, "type", void 0);
85
+ __decorate([
86
+ IsOptional(),
87
+ IsString(),
88
+ MaxLength(50),
89
+ __metadata("design:type", String)
90
+ ], PostRequestDto.prototype, "bodyFormat", void 0);
91
+ __decorate([
92
+ IsOptional(),
93
+ IsString(),
94
+ MaxLength(1000),
95
+ __metadata("design:type", String)
96
+ ], PostRequestDto.prototype, "title", void 0);
97
+ __decorate([
98
+ IsOptional(),
99
+ IsString(),
100
+ MaxLength(5000),
101
+ __metadata("design:type", String)
102
+ ], PostRequestDto.prototype, "description", void 0);
103
+ __decorate([
104
+ IsOptional(),
105
+ IsMediaInput(),
106
+ __metadata("design:type", Object)
107
+ ], PostRequestDto.prototype, "cover", void 0);
108
+ __decorate([
109
+ IsOptional(),
110
+ IsMediaInput(),
111
+ __metadata("design:type", Object)
112
+ ], PostRequestDto.prototype, "video", void 0);
113
+ __decorate([
114
+ IsOptional(),
115
+ IsMediaInput(),
116
+ __metadata("design:type", Object)
117
+ ], PostRequestDto.prototype, "audio", void 0);
118
+ __decorate([
119
+ IsOptional(),
120
+ IsMediaInput(),
121
+ __metadata("design:type", Object)
122
+ ], PostRequestDto.prototype, "document", void 0);
123
+ __decorate([
124
+ IsOptional(),
125
+ IsMediaInputArray(),
126
+ __metadata("design:type", Array)
127
+ ], PostRequestDto.prototype, "media", void 0);
128
+ __decorate([
129
+ IsOptional(),
130
+ IsString(),
131
+ __metadata("design:type", String)
132
+ ], PostRequestDto.prototype, "account", void 0);
133
+ __decorate([
134
+ IsOptional(),
135
+ IsChannelId(),
136
+ __metadata("design:type", Object)
137
+ ], PostRequestDto.prototype, "channelId", void 0);
138
+ __decorate([
139
+ IsOptional(),
140
+ IsObject(),
141
+ __metadata("design:type", Object)
142
+ ], PostRequestDto.prototype, "auth", void 0);
143
+ __decorate([
144
+ IsOptional(),
145
+ IsObject(),
146
+ __metadata("design:type", Object)
147
+ ], PostRequestDto.prototype, "options", void 0);
148
+ __decorate([
149
+ IsOptional(),
150
+ IsBoolean(),
151
+ __metadata("design:type", Boolean)
152
+ ], PostRequestDto.prototype, "disableNotification", void 0);
153
+ __decorate([
154
+ IsOptional(),
155
+ IsArray(),
156
+ ArrayMaxSize(200),
157
+ IsString({ each: true }),
158
+ MaxLength(300, { each: true }),
159
+ __metadata("design:type", Array)
160
+ ], PostRequestDto.prototype, "tags", void 0);
161
+ __decorate([
162
+ IsOptional(),
163
+ IsString(),
164
+ MaxLength(50),
165
+ __metadata("design:type", String)
166
+ ], PostRequestDto.prototype, "scheduledAt", void 0);
167
+ __decorate([
168
+ IsOptional(),
169
+ IsString(),
170
+ MaxLength(50),
171
+ __metadata("design:type", String)
172
+ ], PostRequestDto.prototype, "postLanguage", void 0);
173
+ __decorate([
174
+ IsOptional(),
175
+ IsEnum(['publish', 'draft']),
176
+ __metadata("design:type", String)
177
+ ], PostRequestDto.prototype, "mode", void 0);
178
+ __decorate([
179
+ IsOptional(),
180
+ IsString(),
181
+ MaxLength(1000),
182
+ __metadata("design:type", String)
183
+ ], PostRequestDto.prototype, "idempotencyKey", void 0);
184
+ __decorate([
185
+ IsOptional(),
186
+ IsNumber(),
187
+ Min(1),
188
+ Max(MAX_BODY_LIMIT),
189
+ __metadata("design:type", Number)
190
+ ], PostRequestDto.prototype, "maxBody", void 0);
191
+ PostRequestDto = __decorate([
192
+ HasContent()
193
+ ], PostRequestDto);
194
+ export { PostRequestDto };
195
+ //# sourceMappingURL=post-request.dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-request.dto.js","sourceRoot":"","sources":["../../../../../src/modules/post/dto/post-request.dto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { PostType } from '../../../common/enums/index.js';
2
+ /**
3
+ * Successful post publication response
4
+ */
5
+ export interface PostResponseDto {
6
+ success: true;
7
+ data: {
8
+ /** Platform-specific post ID */
9
+ postId: string;
10
+ /** Public URL to the post (if available) */
11
+ url?: string;
12
+ /** Platform name */
13
+ platform: string;
14
+ /** Actual post type used */
15
+ type: PostType;
16
+ /** Publication timestamp (ISO 8601) */
17
+ publishedAt: string;
18
+ /** Raw response from platform API */
19
+ raw?: Record<string, any>;
20
+ /** Unique request identifier for tracking */
21
+ requestId: string;
22
+ };
23
+ }
24
+ /**
25
+ * Error response for failed post publication
26
+ */
27
+ export interface ErrorResponseDto {
28
+ success: false;
29
+ error: {
30
+ /** Error code for categorization */
31
+ code: string;
32
+ /** Human-readable error message */
33
+ message: string;
34
+ /** Additional error details */
35
+ details?: Record<string, any>;
36
+ /** Raw error response from platform API */
37
+ raw?: Record<string, any>;
38
+ /** Unique request identifier for tracking */
39
+ requestId: string;
40
+ };
41
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=post-response.dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-response.dto.js","sourceRoot":"","sources":["../../../../../src/modules/post/dto/post-response.dto.ts"],"names":[],"mappings":""}
@@ -0,0 +1,33 @@
1
+ import type { PostType } from '../../../common/enums/index.js';
2
+ /**
3
+ * Successful preview response
4
+ */
5
+ export interface PreviewResponseDto {
6
+ success: true;
7
+ data: {
8
+ valid: true;
9
+ /** Detected post type based on content */
10
+ detectedType: PostType;
11
+ /** Body content after conversion */
12
+ convertedBody?: string;
13
+ /** Target format after conversion */
14
+ targetFormat: string;
15
+ /** Length of converted body */
16
+ convertedBodyLength?: number;
17
+ /** Validation warnings (non-blocking) */
18
+ warnings: string[];
19
+ };
20
+ }
21
+ /**
22
+ * Preview error response for invalid requests
23
+ */
24
+ export interface PreviewErrorResponseDto {
25
+ success: false;
26
+ data: {
27
+ valid: false;
28
+ /** Validation errors (blocking) */
29
+ errors: string[];
30
+ /** Validation warnings (non-blocking) */
31
+ warnings: string[];
32
+ };
33
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=preview-response.dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-response.dto.js","sourceRoot":"","sources":["../../../../../src/modules/post/dto/preview-response.dto.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
1
+ import { OnModuleDestroy } from '@nestjs/common';
2
+ import { Cache } from '@nestjs/cache-manager';
3
+ import { AppConfigService } from '../app-config/app-config.service.js';
4
+ import { PostRequestDto, PostResponseDto, ErrorResponseDto } from './dto/index.js';
5
+ /**
6
+ * Idempotency record stored in cache
7
+ * Tracks request processing status and cached response
8
+ */
9
+ interface IdempotencyRecord {
10
+ status: 'processing' | 'completed';
11
+ response?: PostResponseDto | ErrorResponseDto;
12
+ }
13
+ export declare class IdempotencyService implements OnModuleDestroy {
14
+ private readonly cache;
15
+ private readonly appConfig;
16
+ /** Default TTL for idempotency records in cache (10 minutes) */
17
+ private static readonly DEFAULT_IDEMPOTENCY_TTL_MINUTES;
18
+ /** Cleanup interval in milliseconds (5 minutes) */
19
+ private static readonly CLEANUP_INTERVAL_MS;
20
+ private readonly logger;
21
+ /** In-memory map used for atomic idempotency lock management within a single process */
22
+ private readonly records;
23
+ /** Interval handle for periodic cleanup */
24
+ private cleanupInterval?;
25
+ constructor(cache: Cache, appConfig: AppConfigService);
26
+ /**
27
+ * Try to acquire processing lock for the given key or return existing record state.
28
+ * This method is fully in-memory and synchronous, so it is safe from race conditions
29
+ * inside a single Node.js process.
30
+ *
31
+ * @param key - Idempotency cache key
32
+ * @returns
33
+ * - { acquired: true, status: 'processing' } when current request becomes the owner
34
+ * - { acquired: false, status: 'processing' } when another request is already processing
35
+ * - { acquired: false, status: 'completed', response } when a completed response is cached
36
+ */
37
+ acquireLock(key: string): {
38
+ acquired: true;
39
+ status: 'processing';
40
+ } | {
41
+ acquired: false;
42
+ status: 'processing';
43
+ } | {
44
+ acquired: false;
45
+ status: 'completed';
46
+ response: PostResponseDto | ErrorResponseDto;
47
+ };
48
+ /**
49
+ * Build idempotency cache key from request
50
+ * Combines user-provided key with content hash for uniqueness
51
+ * @param request - Post request
52
+ * @returns Cache key or null if no idempotencyKey provided
53
+ */
54
+ buildKey(request: PostRequestDto): string | null;
55
+ /**
56
+ * Get idempotency record from cache
57
+ * @param key - Idempotency cache key
58
+ * @returns Cached record or undefined if not found
59
+ */
60
+ getRecord(key: string): Promise<IdempotencyRecord | undefined>;
61
+ /**
62
+ * Mark request as processing in cache
63
+ * Prevents duplicate processing of the same request
64
+ * @param key - Idempotency cache key
65
+ */
66
+ setProcessing(key: string): Promise<void>;
67
+ /**
68
+ * Store completed request response in cache
69
+ * @param key - Idempotency cache key
70
+ * @param response - Response to cache (success or error)
71
+ */
72
+ setCompleted(key: string, response: PostResponseDto | ErrorResponseDto): Promise<void>;
73
+ /**
74
+ * Get TTL for idempotency records in milliseconds
75
+ * Uses configured value or default if not set
76
+ * @returns TTL in milliseconds
77
+ */
78
+ private getTtlMs;
79
+ /**
80
+ * Start periodic cleanup of expired records
81
+ * Runs every 5 minutes to prevent memory leaks
82
+ */
83
+ private startPeriodicCleanup;
84
+ /**
85
+ * Cleanup expired records from in-memory map
86
+ * Called periodically to prevent memory leaks
87
+ */
88
+ private cleanupExpiredRecords;
89
+ /**
90
+ * Called by NestJS when module is being destroyed
91
+ * Cleanup interval and clear all records
92
+ */
93
+ onModuleDestroy(): void;
94
+ }
95
+ export {};
@@ -0,0 +1,229 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ var IdempotencyService_1;
14
+ import { Injectable, Inject, Logger } from '@nestjs/common';
15
+ import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
16
+ import { createHash } from 'crypto';
17
+ import { AppConfigService } from '../app-config/app-config.service.js';
18
+ let IdempotencyService = class IdempotencyService {
19
+ static { IdempotencyService_1 = this; }
20
+ cache;
21
+ appConfig;
22
+ /** Default TTL for idempotency records in cache (10 minutes) */
23
+ static DEFAULT_IDEMPOTENCY_TTL_MINUTES = 10;
24
+ /** Cleanup interval in milliseconds (5 minutes) */
25
+ static CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
26
+ logger = new Logger(IdempotencyService_1.name);
27
+ /** In-memory map used for atomic idempotency lock management within a single process */
28
+ records = new Map();
29
+ /** Interval handle for periodic cleanup */
30
+ cleanupInterval;
31
+ constructor(cache, appConfig) {
32
+ this.cache = cache;
33
+ this.appConfig = appConfig;
34
+ // Start periodic cleanup of expired records
35
+ this.startPeriodicCleanup();
36
+ }
37
+ /**
38
+ * Try to acquire processing lock for the given key or return existing record state.
39
+ * This method is fully in-memory and synchronous, so it is safe from race conditions
40
+ * inside a single Node.js process.
41
+ *
42
+ * @param key - Idempotency cache key
43
+ * @returns
44
+ * - { acquired: true, status: 'processing' } when current request becomes the owner
45
+ * - { acquired: false, status: 'processing' } when another request is already processing
46
+ * - { acquired: false, status: 'completed', response } when a completed response is cached
47
+ */
48
+ acquireLock(key) {
49
+ const now = Date.now();
50
+ const ttlMs = this.getTtlMs();
51
+ const existing = this.records.get(key);
52
+ if (existing) {
53
+ if (existing.expiresAt <= now) {
54
+ this.records.delete(key);
55
+ }
56
+ else if (existing.status === 'processing') {
57
+ return { acquired: false, status: 'processing' };
58
+ }
59
+ else if (existing.status === 'completed' && existing.response) {
60
+ return {
61
+ acquired: false,
62
+ status: 'completed',
63
+ response: existing.response,
64
+ };
65
+ }
66
+ }
67
+ const record = {
68
+ status: 'processing',
69
+ expiresAt: now + ttlMs,
70
+ };
71
+ this.records.set(key, record);
72
+ return { acquired: true, status: 'processing' };
73
+ }
74
+ /**
75
+ * Build idempotency cache key from request
76
+ * Combines user-provided key with content hash for uniqueness
77
+ * @param request - Post request
78
+ * @returns Cache key or null if no idempotencyKey provided
79
+ */
80
+ buildKey(request) {
81
+ if (!request.idempotencyKey) {
82
+ return null;
83
+ }
84
+ const hash = createHash('sha256')
85
+ .update(JSON.stringify({
86
+ platform: request.platform,
87
+ account: request.account,
88
+ auth: request.auth,
89
+ body: request.body,
90
+ type: request.type,
91
+ media: request.media,
92
+ video: request.video,
93
+ audio: request.audio,
94
+ document: request.document,
95
+ cover: request.cover,
96
+ options: request.options,
97
+ }))
98
+ .digest('hex');
99
+ return `idempotency:${request.idempotencyKey}:${hash}`;
100
+ }
101
+ /**
102
+ * Get idempotency record from cache
103
+ * @param key - Idempotency cache key
104
+ * @returns Cached record or undefined if not found
105
+ */
106
+ async getRecord(key) {
107
+ try {
108
+ const record = await this.cache.get(key);
109
+ if (record !== undefined && record !== null) {
110
+ return record;
111
+ }
112
+ }
113
+ catch {
114
+ // Swallow cache errors and fall back to in-memory records
115
+ }
116
+ const internal = this.records.get(key);
117
+ if (!internal) {
118
+ return undefined;
119
+ }
120
+ if (internal.expiresAt <= Date.now()) {
121
+ this.records.delete(key);
122
+ return undefined;
123
+ }
124
+ return {
125
+ status: internal.status,
126
+ response: internal.response,
127
+ };
128
+ }
129
+ /**
130
+ * Mark request as processing in cache
131
+ * Prevents duplicate processing of the same request
132
+ * @param key - Idempotency cache key
133
+ */
134
+ async setProcessing(key) {
135
+ const ttlMs = this.getTtlMs();
136
+ const record = {
137
+ status: 'processing',
138
+ expiresAt: Date.now() + ttlMs,
139
+ };
140
+ this.records.set(key, record);
141
+ try {
142
+ await this.cache.set(key, { status: 'processing' }, ttlMs);
143
+ }
144
+ catch {
145
+ // Best-effort idempotency: ignore cache backend errors
146
+ }
147
+ }
148
+ /**
149
+ * Store completed request response in cache
150
+ * @param key - Idempotency cache key
151
+ * @param response - Response to cache (success or error)
152
+ */
153
+ async setCompleted(key, response) {
154
+ const ttlMs = this.getTtlMs();
155
+ const record = {
156
+ status: 'completed',
157
+ response,
158
+ expiresAt: Date.now() + ttlMs,
159
+ };
160
+ this.records.set(key, record);
161
+ try {
162
+ await this.cache.set(key, { status: 'completed', response }, ttlMs);
163
+ }
164
+ catch {
165
+ // Best-effort idempotency: ignore cache backend errors
166
+ }
167
+ }
168
+ /**
169
+ * Get TTL for idempotency records in milliseconds
170
+ * Uses configured value or default if not set
171
+ * @returns TTL in milliseconds
172
+ */
173
+ getTtlMs() {
174
+ const minutes = typeof this.appConfig.idempotencyTtlMinutes === 'number'
175
+ ? this.appConfig.idempotencyTtlMinutes
176
+ : IdempotencyService_1.DEFAULT_IDEMPOTENCY_TTL_MINUTES;
177
+ return minutes * 60_000;
178
+ }
179
+ /**
180
+ * Start periodic cleanup of expired records
181
+ * Runs every 5 minutes to prevent memory leaks
182
+ */
183
+ startPeriodicCleanup() {
184
+ this.cleanupInterval = setInterval(() => {
185
+ this.cleanupExpiredRecords();
186
+ }, IdempotencyService_1.CLEANUP_INTERVAL_MS);
187
+ // Don't prevent Node.js from exiting
188
+ this.cleanupInterval.unref();
189
+ this.logger.log(`Started periodic cleanup (interval: ${IdempotencyService_1.CLEANUP_INTERVAL_MS}ms)`, 'IdempotencyService');
190
+ }
191
+ /**
192
+ * Cleanup expired records from in-memory map
193
+ * Called periodically to prevent memory leaks
194
+ */
195
+ cleanupExpiredRecords() {
196
+ const now = Date.now();
197
+ let cleanedCount = 0;
198
+ for (const [key, record] of this.records.entries()) {
199
+ if (record.expiresAt <= now) {
200
+ this.records.delete(key);
201
+ cleanedCount++;
202
+ }
203
+ }
204
+ if (cleanedCount > 0) {
205
+ this.logger.debug(`Cleaned up ${cleanedCount} expired idempotency records. Remaining: ${this.records.size}`, 'IdempotencyService');
206
+ }
207
+ }
208
+ /**
209
+ * Called by NestJS when module is being destroyed
210
+ * Cleanup interval and clear all records
211
+ */
212
+ onModuleDestroy() {
213
+ if (this.cleanupInterval) {
214
+ clearInterval(this.cleanupInterval);
215
+ this.cleanupInterval = undefined;
216
+ }
217
+ const recordCount = this.records.size;
218
+ this.records.clear();
219
+ this.logger.log(`IdempotencyService destroyed. Cleared ${recordCount} records.`, 'IdempotencyService');
220
+ }
221
+ };
222
+ IdempotencyService = IdempotencyService_1 = __decorate([
223
+ Injectable(),
224
+ __param(0, Inject(CACHE_MANAGER)),
225
+ __metadata("design:paramtypes", [Cache,
226
+ AppConfigService])
227
+ ], IdempotencyService);
228
+ export { IdempotencyService };
229
+ //# sourceMappingURL=idempotency.service.js.map