@messenger-box/platform-server 10.0.3-alpha.7 → 10.0.3-alpha.72
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.
- package/lib/containers/containers.js +4 -1
- package/lib/containers/containers.js.map +1 -1
- package/lib/containers/context-services-from-container.d.ts +1 -1
- package/lib/containers/context-services-from-container.js +1 -1
- package/lib/containers/context-services-from-container.js.map +1 -1
- package/lib/graphql/resolvers/channel-member.d.ts +3 -2
- package/lib/graphql/resolvers/channel-member.js +30 -5
- package/lib/graphql/resolvers/channel-member.js.map +1 -1
- package/lib/graphql/resolvers/channel.d.ts +3 -2
- package/lib/graphql/resolvers/channel.js +279 -53
- package/lib/graphql/resolvers/channel.js.map +1 -1
- package/lib/graphql/resolvers/extended-token-account.d.ts +3 -2
- package/lib/graphql/resolvers/extended-token-account.js +90 -23
- package/lib/graphql/resolvers/extended-token-account.js.map +1 -1
- package/lib/graphql/resolvers/index.d.ts +1 -1
- package/lib/graphql/resolvers/post-thread.d.ts +1 -1
- package/lib/graphql/resolvers/post-thread.js +294 -132
- package/lib/graphql/resolvers/post-thread.js.map +1 -1
- package/lib/graphql/resolvers/post.d.ts +2 -3
- package/lib/graphql/resolvers/post.js +696 -234
- package/lib/graphql/resolvers/post.js.map +1 -1
- package/lib/graphql/resolvers/reaction.d.ts +3 -2
- package/lib/graphql/resolvers/reaction.js +96 -14
- package/lib/graphql/resolvers/reaction.js.map +1 -1
- package/lib/graphql/schema/channel-member.graphql +110 -21
- package/lib/graphql/schema/channel-member.graphql.js +1 -1
- package/lib/graphql/schema/channel.graphql +337 -38
- package/lib/graphql/schema/channel.graphql.js +1 -1
- package/lib/graphql/schema/post-thread.graphql +167 -21
- package/lib/graphql/schema/post-thread.graphql.js +1 -1
- package/lib/graphql/schema/post.graphql +284 -40
- package/lib/graphql/schema/post.graphql.js +1 -1
- package/lib/graphql/schema/reaction.graphql +71 -13
- package/lib/graphql/schema/reaction.graphql.js +1 -1
- package/lib/graphql/schema/services.graphql +2 -0
- package/lib/graphql/schema/users.graphql +76 -13
- package/lib/graphql/schema/users.graphql.js +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/interfaces/index.d.ts +0 -1
- package/lib/interfaces/services.d.ts +1 -1
- package/lib/migrations/dbMigrations/AddPostsConfigurationsMigration.d.ts +17 -0
- package/lib/migrations/dbMigrations/AddPostsConfigurationsMigration.js +44 -0
- package/lib/migrations/dbMigrations/AddPostsConfigurationsMigration.js.map +1 -0
- package/lib/migrations/dbMigrations/index.d.ts +1 -0
- package/lib/migrations/index.d.ts +1 -0
- package/lib/migrations/mail-template-migration.js +1 -1
- package/lib/migrations/message-notification-template-migration.d.ts +1 -1
- package/lib/migrations/message-notification-template-migration.js +1 -1
- package/lib/plugins/channel-moleculer-service.d.ts +21 -1
- package/lib/plugins/channel-moleculer-service.js +417 -115
- package/lib/plugins/channel-moleculer-service.js.map +1 -1
- package/lib/plugins/extended-token-account-moleculer-service.d.ts +25 -1
- package/lib/plugins/extended-token-account-moleculer-service.js +348 -22
- package/lib/plugins/extended-token-account-moleculer-service.js.map +1 -1
- package/lib/plugins/messenger-notification-moleculer-service.d.ts +27 -3
- package/lib/plugins/messenger-notification-moleculer-service.js +404 -58
- package/lib/plugins/messenger-notification-moleculer-service.js.map +1 -1
- package/lib/plugins/post-moleculer-service.d.ts +85 -21
- package/lib/plugins/post-moleculer-service.js +986 -256
- package/lib/plugins/post-moleculer-service.js.map +1 -1
- package/lib/plugins/post-thread-moleculer-service.d.ts +33 -1
- package/lib/plugins/post-thread-moleculer-service.js +326 -8
- package/lib/plugins/post-thread-moleculer-service.js.map +1 -1
- package/lib/plugins/reaction-moleculer-service.js +1 -1
- package/lib/plugins/reaction-moleculer-service.js.map +1 -1
- package/lib/preferences/settings/post-settings.d.ts +2 -0
- package/lib/preferences/settings/post-settings.js +47 -9
- package/lib/preferences/settings/post-settings.js.map +1 -1
- package/lib/services/channel-service.d.ts +179 -33
- package/lib/services/channel-service.js +821 -274
- package/lib/services/channel-service.js.map +1 -1
- package/lib/services/extended-token-account-service.d.ts +130 -14
- package/lib/services/extended-token-account-service.js +462 -52
- package/lib/services/extended-token-account-service.js.map +1 -1
- package/lib/services/index.d.ts +1 -0
- package/lib/services/messenger-notification-service.d.ts +106 -13
- package/lib/services/messenger-notification-service.js +824 -442
- package/lib/services/messenger-notification-service.js.map +1 -1
- package/lib/services/post-service.d.ts +182 -16
- package/lib/services/post-service.js +731 -115
- package/lib/services/post-service.js.map +1 -1
- package/lib/services/post-thread-service.d.ts +114 -5
- package/lib/services/post-thread-service.js +400 -13
- package/lib/services/post-thread-service.js.map +1 -1
- package/lib/services/proxy-services/channel-microservice.d.ts +5 -3
- package/lib/services/proxy-services/channel-microservice.js +19 -10
- package/lib/services/proxy-services/channel-microservice.js.map +1 -1
- package/lib/services/proxy-services/messenger-notification-microservice.d.ts +128 -8
- package/lib/services/proxy-services/messenger-notification-microservice.js +324 -29
- package/lib/services/proxy-services/messenger-notification-microservice.js.map +1 -1
- package/lib/services/proxy-services/post-microservice.d.ts +186 -12
- package/lib/services/proxy-services/post-microservice.js +543 -54
- package/lib/services/proxy-services/post-microservice.js.map +1 -1
- package/lib/services/proxy-services/post-thread-microservice.d.ts +134 -3
- package/lib/services/proxy-services/post-thread-microservice.js +388 -6
- package/lib/services/proxy-services/post-thread-microservice.js.map +1 -1
- package/lib/services/proxy-services/reaction-microservice.d.ts +161 -3
- package/lib/services/proxy-services/reaction-microservice.js +474 -2
- package/lib/services/proxy-services/reaction-microservice.js.map +1 -1
- package/lib/services/reaction-service.d.ts +124 -4
- package/lib/services/reaction-service.js +415 -3
- package/lib/services/reaction-service.js.map +1 -1
- package/lib/services/redis-cache-manager.d.ts +18 -0
- package/lib/services/redis-cache-manager.js +83 -0
- package/lib/services/redis-cache-manager.js.map +1 -0
- package/lib/store/models/account-token-store.d.ts +1 -1
- package/lib/store/models/account-token-store.js.map +1 -1
- package/lib/store/models/channel.d.ts +2 -3
- package/lib/store/models/channel.js +181 -72
- package/lib/store/models/channel.js.map +1 -1
- package/lib/store/models/post-thread.d.ts +3 -3
- package/lib/store/models/post-thread.js +96 -14
- package/lib/store/models/post-thread.js.map +1 -1
- package/lib/store/models/post.d.ts +2 -3
- package/lib/store/models/post.js +143 -23
- package/lib/store/models/post.js.map +1 -1
- package/lib/store/models/reaction.d.ts +2 -3
- package/lib/store/models/reaction.js +67 -8
- package/lib/store/models/reaction.js.map +1 -1
- package/lib/store/repositories/__tests__/__fixtures__/team-repository.d.ts +3 -3
- package/lib/store/repositories/channel-repository.d.ts +6 -6
- package/lib/store/repositories/channel-repository.js +5 -2
- package/lib/store/repositories/channel-repository.js.map +1 -1
- package/lib/store/repositories/post-repository.d.ts +6 -6
- package/lib/store/repositories/post-repository.js +5 -2
- package/lib/store/repositories/post-repository.js.map +1 -1
- package/lib/store/repositories/post-thread-repository.d.ts +6 -6
- package/lib/store/repositories/post-thread-repository.js +5 -2
- package/lib/store/repositories/post-thread-repository.js.map +1 -1
- package/lib/store/repositories/reaction-repository.d.ts +6 -6
- package/lib/store/repositories/reaction-repository.js +5 -2
- package/lib/store/repositories/reaction-repository.js.map +1 -1
- package/lib/templates/constants/SERVER_TYPES.ts.template +0 -3
- package/lib/templates/repositories/ChannelRepository.ts.template +3 -3
- package/lib/templates/repositories/PostRepository.ts.template +3 -3
- package/lib/templates/repositories/PostThreadRepository.ts.template +3 -3
- package/lib/templates/repositories/ReactionRepository.ts.template +3 -4
- package/lib/templates/services/ChannelService.ts.template +280 -39
- package/lib/templates/services/ExtendedTokenAccountService.ts.template +104 -9
- package/lib/templates/services/MessengerNotificationService.ts.template +94 -19
- package/lib/templates/services/PostService.ts.template +184 -20
- package/lib/templates/services/PostThreadService.ts.template +151 -6
- package/lib/templates/services/ReactionService.ts.template +129 -3
- package/lib/templates/services/RedisCacheManager.ts.template +22 -0
- package/package.json +6 -5
- package/lib/interfaces/context.d.ts +0 -14
- package/lib/store/models/common-options.js +0 -20
- package/lib/store/models/common-options.js.map +0 -1
|
@@ -1,55 +1,224 @@
|
|
|
1
|
-
import {__decorate,__param,__metadata}from'tslib';import'@cdm-logger/core';import {inject}from'inversify';import {set}from'lodash-es';import {SERVER_TYPES,FileRefType}from'common';import {
|
|
2
|
-
|
|
1
|
+
import {__decorate,__param,__metadata}from'tslib';import'@cdm-logger/core';import {inject}from'inversify';import {set}from'lodash-es';import {SERVER_TYPES,FileRefType}from'common/server';import {BaseService2}from'@common-stack/store-mongo';import {PubSubEngine}from'graphql-subscriptions';import {CommonType}from'@common-stack/core';import {ServiceBroker}from'moleculer';import {DisposableCollection}from'@adminide-stack/core';import {ChannelRepository}from'../store/repositories/channel-repository.js';import {PostRepository}from'../store/repositories/post-repository.js';import'../store/repositories/post-thread-repository.js';import'../store/repositories/reaction-repository.js';import {PostTypes}from'../preferences/settings/post-settings.js';import'../preferences/permissions/inbox-permission-contribution.js';import'../preferences/permissions/inbox-roles-permission-overwrite.js';import {config}from'../config/env-config.js';var PostService_1;
|
|
2
|
+
/**
|
|
3
|
+
* Post Service Implementation
|
|
4
|
+
*
|
|
5
|
+
* This service handles comprehensive post management within the messenger platform,
|
|
6
|
+
* providing operations for creating, updating, managing posts and their associated
|
|
7
|
+
* content including file attachments, notifications, threading, and real-time
|
|
8
|
+
* message delivery across channels and organizations.
|
|
9
|
+
*
|
|
10
|
+
* Key capabilities:
|
|
11
|
+
* - Post lifecycle management (creation, updates, deletion)
|
|
12
|
+
* - File attachment handling and upload management
|
|
13
|
+
* - Real-time message delivery and notifications
|
|
14
|
+
* - Post threading and conversation management
|
|
15
|
+
* - Message status tracking (read, delivered)
|
|
16
|
+
* - Channel integration and message counting
|
|
17
|
+
* - Expo push notification integration
|
|
18
|
+
* - File upload link generation and management
|
|
19
|
+
*/
|
|
20
|
+
// @ts-ignore - Type compatibility issue between BaseService2 and IPostService
|
|
21
|
+
let PostService = class PostService extends BaseService2 {
|
|
22
|
+
static {
|
|
23
|
+
PostService_1 = this;
|
|
24
|
+
}
|
|
3
25
|
channelRepo;
|
|
4
26
|
fileInfoService;
|
|
5
27
|
messengerNotificationService;
|
|
6
28
|
broker;
|
|
7
29
|
pubsub;
|
|
30
|
+
redisCacheManager;
|
|
31
|
+
redisClient;
|
|
32
|
+
toDispose = new DisposableCollection();
|
|
8
33
|
logger;
|
|
9
|
-
|
|
34
|
+
// Redis cache configuration for posts
|
|
35
|
+
static CACHE_TTL = {
|
|
36
|
+
maxAge: 5 * 60,
|
|
37
|
+
scope: 'PUBLIC'
|
|
38
|
+
};
|
|
39
|
+
constructor(repository, channelRepo, fileInfoService, messengerNotificationService, broker, pubsub, redisCacheManager, redisClient, logger) {
|
|
10
40
|
super(repository);
|
|
11
41
|
this.channelRepo = channelRepo;
|
|
12
42
|
this.fileInfoService = fileInfoService;
|
|
13
43
|
this.messengerNotificationService = messengerNotificationService;
|
|
14
44
|
this.broker = broker;
|
|
15
45
|
this.pubsub = pubsub;
|
|
46
|
+
this.redisCacheManager = redisCacheManager;
|
|
47
|
+
this.redisClient = redisClient;
|
|
16
48
|
this.logger = logger?.child({
|
|
17
49
|
className: PostService_1.name
|
|
18
50
|
});
|
|
19
51
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Disposes of resources used by the service
|
|
54
|
+
*/
|
|
55
|
+
dispose() {
|
|
56
|
+
this.toDispose.dispose();
|
|
57
|
+
}
|
|
58
|
+
// Override base service methods to handle errors internally
|
|
59
|
+
async create(data) {
|
|
60
|
+
try {
|
|
61
|
+
this.logger.debug('Creating post', {
|
|
62
|
+
data
|
|
63
|
+
});
|
|
64
|
+
// Validate required fields
|
|
65
|
+
if (!data) {
|
|
66
|
+
throw new Error('Post data is required');
|
|
67
|
+
}
|
|
68
|
+
if (!data.channel) {
|
|
69
|
+
throw new Error('Channel is required for post creation');
|
|
70
|
+
}
|
|
71
|
+
if (!data.author) {
|
|
72
|
+
throw new Error('Author is required for post creation');
|
|
73
|
+
}
|
|
74
|
+
// Add timestamps and default type
|
|
75
|
+
const postData = {
|
|
76
|
+
...data,
|
|
77
|
+
type: data.type || 'Simple',
|
|
78
|
+
// Default to Simple type for regular messages
|
|
79
|
+
createdAt: new Date(),
|
|
80
|
+
updatedAt: new Date()
|
|
81
|
+
};
|
|
82
|
+
// Create the post using repository
|
|
83
|
+
// const result = await this.repository.create(postData);
|
|
84
|
+
const result = await super.create(postData);
|
|
85
|
+
if (!result) {
|
|
86
|
+
throw new Error('Failed to create post');
|
|
87
|
+
}
|
|
88
|
+
// Update channel statistics
|
|
89
|
+
try {
|
|
90
|
+
await this.channelRepo.model.updateOne({
|
|
91
|
+
_id: data.channel
|
|
92
|
+
}, {
|
|
93
|
+
lastPostAt: result.createdAt,
|
|
94
|
+
$inc: {
|
|
95
|
+
totalMsgCount: 1,
|
|
96
|
+
totalMsgCountRoot: 1,
|
|
97
|
+
'members.$[elem].msgCountRoot': 1,
|
|
98
|
+
'members.$[elem].msgCount': 1
|
|
99
|
+
}
|
|
100
|
+
}, {
|
|
101
|
+
arrayFilters: [{
|
|
102
|
+
'elem.user': data.author
|
|
103
|
+
}]
|
|
104
|
+
});
|
|
105
|
+
} catch (channelError) {
|
|
106
|
+
this.logger.error('Error updating channel stats:', channelError);
|
|
107
|
+
// Don't fail the post creation if stats update fails
|
|
108
|
+
}
|
|
109
|
+
await this.pubsub.publish(`POST_CREATED.${data.channel}`, result);
|
|
110
|
+
await this.sendExpoNotification(result);
|
|
111
|
+
// Invalidate caches related to this channel
|
|
112
|
+
await this.invalidatePostCachesByChannel(String(data.channel));
|
|
113
|
+
return result;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
this.logger.error('Error creating post:', error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async update(id, data) {
|
|
120
|
+
try {
|
|
121
|
+
const result = await super.update(id, data);
|
|
122
|
+
if (!result || typeof result === 'object' && 'message' in result) {
|
|
123
|
+
this.logger.error('Failed to update post', {
|
|
124
|
+
error: result
|
|
125
|
+
});
|
|
126
|
+
throw new Error('Failed to update post');
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
// Invalidate caches related to this post's channel
|
|
130
|
+
const channelId = result?.channel?.toString?.() || data?.channel;
|
|
131
|
+
if (channelId) {
|
|
132
|
+
await this.invalidatePostCachesByChannel(String(channelId));
|
|
133
|
+
}
|
|
134
|
+
} catch (cacheError) {
|
|
135
|
+
this.logger.warn('Cache invalidation error after post update: %o', cacheError);
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.logger.error('Error updating post', {
|
|
140
|
+
error
|
|
141
|
+
});
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async delete(criteria) {
|
|
146
|
+
try {
|
|
147
|
+
const result = await super.delete(criteria);
|
|
148
|
+
if (typeof result !== 'boolean') {
|
|
149
|
+
this.logger.error('Failed to delete post', {
|
|
150
|
+
error: result
|
|
151
|
+
});
|
|
152
|
+
throw new Error('Failed to delete post');
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
// Attempt to invalidate caches if criteria is id
|
|
156
|
+
const id = typeof criteria === 'string' ? criteria : criteria?.id;
|
|
157
|
+
if (id) {
|
|
158
|
+
const existing = await this.get(id).catch(() => null);
|
|
159
|
+
const channelId = existing?.channel?.toString?.();
|
|
160
|
+
if (channelId) {
|
|
161
|
+
await this.invalidatePostCachesByChannel(String(channelId));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch (cacheError) {
|
|
165
|
+
this.logger.warn('Cache invalidation error after post delete: %o', cacheError);
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
this.logger.error('Error deleting post', {
|
|
170
|
+
error
|
|
171
|
+
});
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Creates a file upload link for a post
|
|
177
|
+
*
|
|
178
|
+
* @description Generates a secure upload URL for attaching files to posts
|
|
179
|
+
*
|
|
180
|
+
* @param {string} postId - The post identifier
|
|
181
|
+
* @param {string} filename - The name of the file to upload
|
|
182
|
+
* @param {string} userId - The user performing the upload
|
|
183
|
+
* @returns {Promise<string | Error>} - Upload URL or error
|
|
184
|
+
*/
|
|
185
|
+
async createFileUploadLink(postId, filename, userId) {
|
|
186
|
+
try {
|
|
187
|
+
if (!postId || !filename || !userId) {
|
|
188
|
+
return new Error('Post ID, filename, and user ID are required');
|
|
189
|
+
}
|
|
190
|
+
const uploadUrl = await this.fileInfoService.uploadByUrl({
|
|
191
|
+
filename,
|
|
192
|
+
refType: FileRefType.Post,
|
|
193
|
+
ref: postId,
|
|
194
|
+
userId
|
|
195
|
+
});
|
|
196
|
+
this.logger.debug('File upload link created', {
|
|
197
|
+
postId,
|
|
198
|
+
filename,
|
|
199
|
+
userId
|
|
200
|
+
});
|
|
201
|
+
return uploadUrl;
|
|
202
|
+
} catch (error) {
|
|
203
|
+
this.logger.error('Error creating file upload link: %o', error);
|
|
204
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while creating upload link');
|
|
205
|
+
}
|
|
27
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Attaches an uploaded file to a post
|
|
209
|
+
*
|
|
210
|
+
* @description Processes and attaches a completed file upload to a post
|
|
211
|
+
*
|
|
212
|
+
* @param {string} postId - The post identifier
|
|
213
|
+
* @param {IUploadedFileInput} file - The uploaded file information
|
|
214
|
+
* @param {string} createdBy - The user who uploaded the file
|
|
215
|
+
* @returns {Promise<IFileInfo | Error>} - File information or error
|
|
216
|
+
*/
|
|
28
217
|
async attachUploadedFile(postId, file, createdBy) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
refType: FileRefType.Post,
|
|
34
|
-
ref: postId,
|
|
35
|
-
createdBy
|
|
36
|
-
});
|
|
37
|
-
const fileResponse = await this.fileInfoService.get(id);
|
|
38
|
-
return {
|
|
39
|
-
...fileResponse,
|
|
40
|
-
url: this.fileInfoService.getDefaultImage(fileResponse.url)
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
createFilesUploadLink(postId, filenames, userId) {
|
|
44
|
-
return Promise.all(filenames.map(filename => this.fileInfoService.uploadByUrl({
|
|
45
|
-
filename,
|
|
46
|
-
refType: FileRefType.Post,
|
|
47
|
-
ref: postId,
|
|
48
|
-
userId
|
|
49
|
-
})));
|
|
50
|
-
}
|
|
51
|
-
attachUploadedFiles(postId, files, createdBy) {
|
|
52
|
-
return Promise.all(files.map(async file => {
|
|
218
|
+
try {
|
|
219
|
+
if (!postId || !file || !createdBy) {
|
|
220
|
+
return new Error('Post ID, file data, and creator ID are required');
|
|
221
|
+
}
|
|
53
222
|
const {
|
|
54
223
|
id
|
|
55
224
|
} = await this.fileInfoService.createUploadedFile({
|
|
@@ -59,103 +228,550 @@ let PostService = PostService_1 = class PostService extends BaseService {
|
|
|
59
228
|
createdBy
|
|
60
229
|
});
|
|
61
230
|
const fileResponse = await this.fileInfoService.get(id);
|
|
62
|
-
|
|
231
|
+
const result = {
|
|
63
232
|
...fileResponse,
|
|
64
233
|
url: this.fileInfoService.getDefaultImage(fileResponse.url)
|
|
65
234
|
};
|
|
66
|
-
|
|
235
|
+
this.logger.debug('File attached to post', {
|
|
236
|
+
postId,
|
|
237
|
+
fileId: id,
|
|
238
|
+
createdBy
|
|
239
|
+
});
|
|
240
|
+
return result;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
this.logger.error('Error attaching uploaded file: %o', error);
|
|
243
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while attaching file');
|
|
244
|
+
}
|
|
67
245
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
'
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Creates upload links for multiple files
|
|
248
|
+
*
|
|
249
|
+
* @description Generates secure upload URLs for multiple files to be attached to a post
|
|
250
|
+
*
|
|
251
|
+
* @param {string} postId - The post identifier
|
|
252
|
+
* @param {string[]} filenames - Array of file names
|
|
253
|
+
* @param {string} userId - The user performing the uploads
|
|
254
|
+
* @returns {Promise<string[] | Error>} - Array of upload URLs or error
|
|
255
|
+
*/
|
|
256
|
+
async createFilesUploadLink(postId, filenames, userId) {
|
|
257
|
+
try {
|
|
258
|
+
if (!postId || !filenames || !Array.isArray(filenames) || !userId) {
|
|
259
|
+
return new Error('Post ID, filenames array, and user ID are required');
|
|
260
|
+
}
|
|
261
|
+
if (filenames.length === 0) {
|
|
262
|
+
return new Error('At least one filename is required');
|
|
263
|
+
}
|
|
264
|
+
const uploadUrls = await Promise.all(filenames.map(filename => this.fileInfoService.uploadByUrl({
|
|
265
|
+
filename,
|
|
266
|
+
refType: FileRefType.Post,
|
|
267
|
+
ref: postId,
|
|
268
|
+
userId
|
|
269
|
+
})));
|
|
270
|
+
this.logger.debug('Multiple file upload links created', {
|
|
271
|
+
postId,
|
|
272
|
+
fileCount: filenames.length,
|
|
273
|
+
userId
|
|
274
|
+
});
|
|
275
|
+
return uploadUrls;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
this.logger.error('Error creating multiple file upload links: %o', error);
|
|
278
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while creating upload links');
|
|
279
|
+
}
|
|
96
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Attaches multiple uploaded files to a post
|
|
283
|
+
*
|
|
284
|
+
* @description Processes and attaches multiple completed file uploads to a post
|
|
285
|
+
*
|
|
286
|
+
* @param {string} postId - The post identifier
|
|
287
|
+
* @param {IUploadedFileInput[]} files - Array of uploaded file information
|
|
288
|
+
* @param {string} createdBy - The user who uploaded the files
|
|
289
|
+
* @returns {Promise<IFileInfo[] | Error>} - Array of file information or error
|
|
290
|
+
*/
|
|
291
|
+
async attachUploadedFiles(postId, files, createdBy) {
|
|
292
|
+
try {
|
|
293
|
+
if (!postId || !files || !Array.isArray(files) || !createdBy) {
|
|
294
|
+
return new Error('Post ID, files array, and creator ID are required');
|
|
295
|
+
}
|
|
296
|
+
if (files.length === 0) {
|
|
297
|
+
return new Error('At least one file is required');
|
|
298
|
+
}
|
|
299
|
+
const fileResults = await Promise.all(files.map(async file => {
|
|
300
|
+
const {
|
|
301
|
+
id
|
|
302
|
+
} = await this.fileInfoService.createUploadedFile({
|
|
303
|
+
...file,
|
|
304
|
+
refType: FileRefType.Post,
|
|
305
|
+
ref: postId,
|
|
306
|
+
createdBy
|
|
307
|
+
});
|
|
308
|
+
const fileResponse = await this.fileInfoService.get(id);
|
|
309
|
+
return {
|
|
310
|
+
...fileResponse,
|
|
311
|
+
url: this.fileInfoService.getDefaultImage(fileResponse.url)
|
|
312
|
+
};
|
|
313
|
+
}));
|
|
314
|
+
this.logger.debug('Multiple files attached to post', {
|
|
315
|
+
postId,
|
|
316
|
+
fileCount: files.length,
|
|
317
|
+
createdBy
|
|
318
|
+
});
|
|
319
|
+
return fileResults;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
this.logger.error('Error attaching multiple uploaded files: %o', error);
|
|
322
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while attaching files');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Creates a new post with full integration
|
|
327
|
+
*
|
|
328
|
+
* @description Creates a post with channel updates, notifications, and real-time publishing
|
|
329
|
+
*
|
|
330
|
+
* @param {IPostServiceInput} data - Post creation data
|
|
331
|
+
* @returns {Promise<AsDomainType<IPostModel> | Error>} - Created post or error
|
|
332
|
+
*/
|
|
333
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
97
334
|
async createWithoutSubscription(data) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
335
|
+
try {
|
|
336
|
+
if (!data) {
|
|
337
|
+
return new Error('Post data is required');
|
|
338
|
+
}
|
|
339
|
+
if (!data.channel) {
|
|
340
|
+
return new Error('Channel is required for post creation');
|
|
341
|
+
}
|
|
342
|
+
if (!data.author) {
|
|
343
|
+
return new Error('Author is required for post creation');
|
|
344
|
+
}
|
|
345
|
+
// Create the post
|
|
346
|
+
const postData = {
|
|
347
|
+
...data,
|
|
348
|
+
createdAt: new Date(),
|
|
349
|
+
updatedAt: new Date()
|
|
350
|
+
}; // Type assertion for compatibility
|
|
351
|
+
const post = await this.repository.create(postData);
|
|
352
|
+
if (!post) {
|
|
353
|
+
return new Error('Failed to create post');
|
|
354
|
+
}
|
|
355
|
+
const {
|
|
356
|
+
channel
|
|
357
|
+
} = data;
|
|
358
|
+
// Update channel statistics without real-time publishing
|
|
359
|
+
// @ts-ignore - Accessing private property for channel updates
|
|
360
|
+
await this.channelRepo.model.updateOne({
|
|
361
|
+
_id: channel
|
|
362
|
+
}, {
|
|
363
|
+
lastPostAt: post.createdAt,
|
|
364
|
+
$inc: {
|
|
365
|
+
totalMsgCount: 1,
|
|
366
|
+
totalMsgCountRoot: 1,
|
|
367
|
+
'members.$[elem].msgCountRoot': 1,
|
|
368
|
+
'members.$[elem].msgCount': 1
|
|
369
|
+
}
|
|
370
|
+
}, {
|
|
371
|
+
arrayFilters: [{
|
|
372
|
+
'elem.user': data.author
|
|
373
|
+
}]
|
|
374
|
+
});
|
|
375
|
+
this.logger.debug('Post created without subscription', {
|
|
376
|
+
postId: post.id,
|
|
377
|
+
channelId: channel,
|
|
378
|
+
authorId: data.author
|
|
379
|
+
});
|
|
380
|
+
// Invalidate caches related to this channel
|
|
381
|
+
await this.invalidatePostCachesByChannel(String(channel));
|
|
382
|
+
return post;
|
|
383
|
+
} catch (error) {
|
|
384
|
+
this.logger.error('Error creating post without subscription: %o', error);
|
|
385
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while creating post');
|
|
386
|
+
}
|
|
118
387
|
}
|
|
119
|
-
|
|
120
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Deletes a file by URL
|
|
390
|
+
*
|
|
391
|
+
* @description Removes a file from storage using its URL
|
|
392
|
+
*
|
|
393
|
+
* @param {string} url - The file URL to delete
|
|
394
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
395
|
+
*/
|
|
396
|
+
async deleteFile(url) {
|
|
397
|
+
try {
|
|
398
|
+
if (!url) {
|
|
399
|
+
return new Error('File URL is required');
|
|
400
|
+
}
|
|
401
|
+
const success = await this.fileInfoService.deleteByUrl(url);
|
|
402
|
+
this.logger.debug('File deleted', {
|
|
403
|
+
url,
|
|
404
|
+
success
|
|
405
|
+
});
|
|
406
|
+
return success;
|
|
407
|
+
} catch (error) {
|
|
408
|
+
this.logger.error('Error deleting file: %o', error);
|
|
409
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while deleting file');
|
|
410
|
+
}
|
|
121
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Marks a message as read by a user
|
|
414
|
+
*
|
|
415
|
+
* @description Updates message status to indicate it has been read by a specific user
|
|
416
|
+
*
|
|
417
|
+
* @param {IMessageIdentifier} messageId - Message identifier
|
|
418
|
+
* @param {string} user - User ID who read the message
|
|
419
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
420
|
+
*/
|
|
122
421
|
async readMessage(messageId, user) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
422
|
+
try {
|
|
423
|
+
if (!messageId?.messageId || !user) {
|
|
424
|
+
return new Error('Message ID and user ID are required');
|
|
126
425
|
}
|
|
127
|
-
|
|
128
|
-
|
|
426
|
+
await this.update(messageId.messageId, {
|
|
427
|
+
props: {
|
|
428
|
+
[`[${user}]`]: set({}, PostTypes.isRead, true)
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
this.logger.debug('Message marked as read', {
|
|
432
|
+
messageId: messageId.messageId,
|
|
433
|
+
userId: user
|
|
434
|
+
});
|
|
435
|
+
return true;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
this.logger.error('Error marking message as read: %o', error);
|
|
438
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while marking message as read');
|
|
439
|
+
}
|
|
129
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Marks a message as delivered to a user
|
|
443
|
+
*
|
|
444
|
+
* @description Updates message status to indicate it has been delivered to a specific user
|
|
445
|
+
*
|
|
446
|
+
* @param {IMessageIdentifier} messageId - Message identifier
|
|
447
|
+
* @param {string} user - User ID who received the message
|
|
448
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
449
|
+
*/
|
|
130
450
|
async deliverMessage(messageId, user) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
451
|
+
try {
|
|
452
|
+
if (!messageId?.messageId || !user) {
|
|
453
|
+
return new Error('Message ID and user ID are required');
|
|
134
454
|
}
|
|
135
|
-
|
|
136
|
-
|
|
455
|
+
await this.update(messageId.messageId, {
|
|
456
|
+
props: {
|
|
457
|
+
[`[${user}]`]: set({}, PostTypes.isDelivered, true)
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
this.logger.debug('Message marked as delivered', {
|
|
461
|
+
messageId: messageId.messageId,
|
|
462
|
+
userId: user
|
|
463
|
+
});
|
|
464
|
+
return true;
|
|
465
|
+
} catch (error) {
|
|
466
|
+
this.logger.error('Error marking message as delivered: %o', error);
|
|
467
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while marking message as delivered');
|
|
468
|
+
}
|
|
137
469
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
470
|
+
/**
|
|
471
|
+
* Creates a post with associated post thread
|
|
472
|
+
*
|
|
473
|
+
* @description Creates both a post and its associated thread for threaded conversations
|
|
474
|
+
*
|
|
475
|
+
* @param {CreatePostThreadOptions} data - Post and thread creation options
|
|
476
|
+
* @returns {Promise<{post: AsDomainType<IPostModel>; postThread: IPostThread} | Error>} - Created post and thread or error
|
|
477
|
+
*/
|
|
478
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
479
|
+
async createPostWithPostThread(data) {
|
|
480
|
+
try {
|
|
481
|
+
if (!data) {
|
|
482
|
+
return new Error('Post thread creation data is required');
|
|
483
|
+
}
|
|
484
|
+
// This would require integration with PostThreadService
|
|
485
|
+
// For now, return an error indicating it needs implementation
|
|
486
|
+
return new Error('Post thread creation is not yet implemented');
|
|
487
|
+
} catch (error) {
|
|
488
|
+
this.logger.error('Error creating post with thread: %o', error);
|
|
489
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while creating post with thread');
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Retrieves the last message in a channel
|
|
494
|
+
*
|
|
495
|
+
* @description Gets the most recent post/message from a specific channel
|
|
496
|
+
*
|
|
497
|
+
* @param {string} channelId - The channel identifier
|
|
498
|
+
* @returns {Promise<AsDomainType<IPostModel> | Error>} - Last message or error
|
|
499
|
+
*/
|
|
500
|
+
// @ts-ignore - Type compatibility issue between interface and implementation
|
|
501
|
+
async getLastMessage(channelId) {
|
|
502
|
+
try {
|
|
503
|
+
if (!channelId) {
|
|
504
|
+
return new Error('Channel ID is required');
|
|
505
|
+
}
|
|
506
|
+
// Try Redis cache first
|
|
507
|
+
if (this.redisCacheManager) {
|
|
508
|
+
try {
|
|
509
|
+
const cacheKey = `${config.APP_NAME}:getLastPostByChannel`;
|
|
510
|
+
const variables = {
|
|
511
|
+
channelId
|
|
512
|
+
};
|
|
513
|
+
const cacheCtx = {
|
|
514
|
+
userId: 'system'
|
|
515
|
+
};
|
|
516
|
+
const cached = await this.redisCacheManager.get(cacheKey, variables, cacheCtx);
|
|
517
|
+
if (cached) {
|
|
518
|
+
this.logger.debug(`Cache hit for last post by channel ${channelId}`);
|
|
519
|
+
return cached;
|
|
520
|
+
}
|
|
521
|
+
} catch (cacheError) {
|
|
522
|
+
this.logger.warn(`Redis cache error for getLastMessage(${channelId}): ${cacheError?.message}`);
|
|
151
523
|
}
|
|
152
|
-
}
|
|
153
|
-
|
|
524
|
+
}
|
|
525
|
+
// @ts-ignore - Accessing private property for direct query
|
|
526
|
+
const post = await this.repository.model.findOne({
|
|
527
|
+
channel: channelId
|
|
528
|
+
}).sort({
|
|
529
|
+
createdAt: -1
|
|
530
|
+
});
|
|
531
|
+
if (!post) {
|
|
532
|
+
return new Error('No messages found in channel');
|
|
533
|
+
}
|
|
534
|
+
this.logger.debug('Last message retrieved', {
|
|
535
|
+
channelId,
|
|
536
|
+
postId: post._id
|
|
537
|
+
});
|
|
538
|
+
const result = post;
|
|
539
|
+
// Cache the result
|
|
540
|
+
if (this.redisCacheManager) {
|
|
541
|
+
try {
|
|
542
|
+
const cacheKey = `${config.APP_NAME}:getLastPostByChannel`;
|
|
543
|
+
const variables = {
|
|
544
|
+
channelId
|
|
545
|
+
};
|
|
546
|
+
const cacheCtx = {
|
|
547
|
+
userId: 'system'
|
|
548
|
+
};
|
|
549
|
+
await this.redisCacheManager.set(cacheKey, variables, result, cacheCtx, PostService_1.CACHE_TTL);
|
|
550
|
+
} catch (cacheError) {
|
|
551
|
+
this.logger.warn(`Failed to cache last post by channel: ${cacheError?.message}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return result;
|
|
555
|
+
} catch (error) {
|
|
556
|
+
this.logger.error('Error getting last message: %o', error);
|
|
557
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while getting last message');
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Gets posts by channel with pagination
|
|
562
|
+
*
|
|
563
|
+
* @description Retrieves posts from a channel with pagination support
|
|
564
|
+
*
|
|
565
|
+
* @param {string} channelId - The channel identifier
|
|
566
|
+
* @param {number} limit - Maximum number of posts to return
|
|
567
|
+
* @param {number} offset - Number of posts to skip
|
|
568
|
+
* @returns {Promise<Array<AsDomainType<IPostModel>> | Error>} - Array of posts or error
|
|
569
|
+
*/
|
|
570
|
+
async getPostsByChannel(channelId, limit = 50, offset = 0) {
|
|
571
|
+
try {
|
|
572
|
+
if (!channelId) {
|
|
573
|
+
return new Error('Channel ID is required');
|
|
574
|
+
}
|
|
575
|
+
// Try Redis cache first
|
|
576
|
+
if (this.redisCacheManager) {
|
|
577
|
+
try {
|
|
578
|
+
const cacheKey = `${config.APP_NAME}:getPostsByChannel`;
|
|
579
|
+
const variables = {
|
|
580
|
+
channelId,
|
|
581
|
+
limit,
|
|
582
|
+
offset
|
|
583
|
+
};
|
|
584
|
+
const cacheCtx = {
|
|
585
|
+
userId: 'system'
|
|
586
|
+
};
|
|
587
|
+
const cached = await this.redisCacheManager.get(cacheKey, variables, cacheCtx);
|
|
588
|
+
if (cached) {
|
|
589
|
+
this.logger.debug(`Cache hit for posts by channel ${channelId}`);
|
|
590
|
+
return cached;
|
|
591
|
+
}
|
|
592
|
+
} catch (cacheError) {
|
|
593
|
+
this.logger.warn(`Redis cache error for getPostsByChannel(${channelId}): ${cacheError?.message}`);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const posts = await this.getAll({
|
|
597
|
+
criteria: {
|
|
598
|
+
channel: channelId
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
// Apply manual pagination
|
|
602
|
+
const paginatedPosts = posts.slice(offset, offset + limit);
|
|
603
|
+
this.logger.debug('Posts retrieved by channel', {
|
|
604
|
+
channelId,
|
|
605
|
+
count: paginatedPosts.length,
|
|
606
|
+
limit,
|
|
607
|
+
offset
|
|
608
|
+
});
|
|
609
|
+
const result = paginatedPosts;
|
|
610
|
+
// Cache the result
|
|
611
|
+
if (this.redisCacheManager) {
|
|
612
|
+
try {
|
|
613
|
+
const cacheKey = `${config.APP_NAME}:getPostsByChannel`;
|
|
614
|
+
const variables = {
|
|
615
|
+
channelId,
|
|
616
|
+
limit,
|
|
617
|
+
offset
|
|
618
|
+
};
|
|
619
|
+
const cacheCtx = {
|
|
620
|
+
userId: 'system'
|
|
621
|
+
};
|
|
622
|
+
await this.redisCacheManager.set(cacheKey, variables, result, cacheCtx, PostService_1.CACHE_TTL);
|
|
623
|
+
} catch (cacheError) {
|
|
624
|
+
this.logger.warn(`Failed to cache posts by channel: ${cacheError?.message}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return result;
|
|
628
|
+
} catch (error) {
|
|
629
|
+
this.logger.error('Error getting posts by channel: %o', error);
|
|
630
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while getting posts by channel');
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Updates a post
|
|
635
|
+
*
|
|
636
|
+
* @description Updates post content and metadata
|
|
637
|
+
*
|
|
638
|
+
* @param {string} postId - The post identifier
|
|
639
|
+
* @param {Partial<IPostServiceInput>} updates - Update data
|
|
640
|
+
* @returns {Promise<AsDomainType<IPostModel> | Error>} - Updated post or error
|
|
641
|
+
*/
|
|
642
|
+
async updatePost(postId, updates) {
|
|
643
|
+
try {
|
|
644
|
+
if (!postId) {
|
|
645
|
+
return new Error('Post ID is required');
|
|
646
|
+
}
|
|
647
|
+
if (!updates || Object.keys(updates).length === 0) {
|
|
648
|
+
return new Error('Updates are required');
|
|
649
|
+
}
|
|
650
|
+
// Get existing post
|
|
651
|
+
const existingPost = await this.get(postId);
|
|
652
|
+
if (!existingPost) {
|
|
653
|
+
return new Error('Post not found');
|
|
654
|
+
}
|
|
655
|
+
// Prepare update data
|
|
656
|
+
const updateData = {
|
|
657
|
+
...updates,
|
|
658
|
+
updatedAt: new Date()
|
|
659
|
+
}; // Type assertion for compatibility
|
|
660
|
+
// Update the post
|
|
661
|
+
const updatedPost = await this.update(postId, updateData);
|
|
662
|
+
if (!updatedPost) {
|
|
663
|
+
return new Error('Failed to update post');
|
|
664
|
+
}
|
|
665
|
+
this.logger.debug('Post updated successfully', {
|
|
666
|
+
postId,
|
|
667
|
+
updates: Object.keys(updates)
|
|
668
|
+
});
|
|
669
|
+
return updatedPost;
|
|
670
|
+
} catch (error) {
|
|
671
|
+
this.logger.error('Error updating post: %o', error);
|
|
672
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while updating post');
|
|
154
673
|
}
|
|
155
674
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
675
|
+
/**
|
|
676
|
+
* Deletes a post
|
|
677
|
+
*
|
|
678
|
+
* @description Soft deletes a post and its associated data
|
|
679
|
+
*
|
|
680
|
+
* @param {string} postId - The post identifier
|
|
681
|
+
* @returns {Promise<boolean | Error>} - Success status or error
|
|
682
|
+
*/
|
|
683
|
+
async deletePost(postId) {
|
|
684
|
+
try {
|
|
685
|
+
if (!postId) {
|
|
686
|
+
return new Error('Post ID is required');
|
|
687
|
+
}
|
|
688
|
+
// Get post to verify it exists
|
|
689
|
+
const post = await this.get(postId);
|
|
690
|
+
if (!post) {
|
|
691
|
+
return new Error('Post not found');
|
|
692
|
+
}
|
|
693
|
+
// Soft delete the post
|
|
694
|
+
const success = await this.repository.delete({
|
|
695
|
+
id: postId
|
|
696
|
+
});
|
|
697
|
+
if (success) {
|
|
698
|
+
this.logger.debug('Post deleted successfully', {
|
|
699
|
+
postId
|
|
700
|
+
});
|
|
701
|
+
try {
|
|
702
|
+
const channelId = post?.channel?.toString?.();
|
|
703
|
+
if (channelId) {
|
|
704
|
+
await this.invalidatePostCachesByChannel(String(channelId));
|
|
705
|
+
}
|
|
706
|
+
} catch (cacheError) {
|
|
707
|
+
this.logger.warn('Cache invalidation error after deletePost: %o', cacheError);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return success;
|
|
711
|
+
} catch (error) {
|
|
712
|
+
this.logger.error('Error deleting post: %o', error);
|
|
713
|
+
return error instanceof Error ? error : new Error('Unknown error occurred while deleting post');
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Sends Expo push notification for a post
|
|
718
|
+
*
|
|
719
|
+
* @description Handles push notification logic for new posts
|
|
720
|
+
*
|
|
721
|
+
* @param {any} post - The post data
|
|
722
|
+
* @private
|
|
723
|
+
*/
|
|
724
|
+
async sendExpoNotification(post) {
|
|
725
|
+
try {
|
|
726
|
+
if (post?.props?.notificationParams && (!post?.type || post?.type !== 'ALERT')) {
|
|
727
|
+
await this.messengerNotificationService.sendExpoNotificationOnPost(post);
|
|
728
|
+
} else if (!post?.type || post?.type !== 'ALERT') {
|
|
729
|
+
const notificationData = {
|
|
730
|
+
url: config.INBOX_MESSEGE_PATH,
|
|
731
|
+
params: {
|
|
732
|
+
channelId: post?.channel,
|
|
733
|
+
hideTabBar: true
|
|
734
|
+
},
|
|
735
|
+
screen: 'DialogMessages',
|
|
736
|
+
other: {
|
|
737
|
+
sound: 'default'
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
await this.messengerNotificationService.sendExpoNotificationOnPost(post, notificationData);
|
|
741
|
+
}
|
|
742
|
+
this.logger.debug('Expo notification sent', {
|
|
743
|
+
postId: post?.id
|
|
744
|
+
});
|
|
745
|
+
} catch (error) {
|
|
746
|
+
this.logger.error('Error sending Expo notification: %o', error);
|
|
747
|
+
// Don't throw error as notification failure shouldn't break post creation
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Invalidates cache entries related to a channel's posts
|
|
752
|
+
* @param channelId - Channel ID
|
|
753
|
+
*/
|
|
754
|
+
async invalidatePostCachesByChannel(channelId) {
|
|
755
|
+
if (!this.redisCacheManager || !channelId) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
// Invalidate last post by channel
|
|
760
|
+
await this.redisCacheManager.del(`${config.APP_NAME}:getLastPostByChannel`, {
|
|
761
|
+
channelId
|
|
762
|
+
}, {
|
|
763
|
+
userId: 'system'
|
|
764
|
+
}, true);
|
|
765
|
+
// Invalidate posts by channel with any pagination (wildcard delete)
|
|
766
|
+
await this.redisCacheManager.del(`${config.APP_NAME}:getPostsByChannel`, {
|
|
767
|
+
channelId
|
|
768
|
+
}, {
|
|
769
|
+
userId: 'system'
|
|
770
|
+
}, true);
|
|
771
|
+
this.logger.debug(`Invalidated post caches for channel ${channelId}`);
|
|
772
|
+
} catch (error) {
|
|
773
|
+
this.logger.warn(`Error invalidating post caches: ${error?.message}`);
|
|
774
|
+
}
|
|
159
775
|
}
|
|
160
776
|
};
|
|
161
|
-
PostService = PostService_1 = __decorate([__param(0, inject(SERVER_TYPES.PostRepository)), __param(1, inject(SERVER_TYPES.ChannelRepository)), __param(2, inject(SERVER_TYPES.FileInfoService)), __param(3, inject(SERVER_TYPES.MessengerNotificationService)), __param(4, inject(CommonType.MOLECULER_BROKER)), __param(5, inject('PubSub')), __param(6, inject('Logger')), __metadata("design:paramtypes", [PostRepository, ChannelRepository, Object, Object, ServiceBroker, PubSubEngine, Object])], PostService);export{PostService};//# sourceMappingURL=post-service.js.map
|
|
777
|
+
PostService = PostService_1 = __decorate([__param(0, inject(SERVER_TYPES.PostRepository)), __param(1, inject(SERVER_TYPES.ChannelRepository)), __param(2, inject(SERVER_TYPES.FileInfoService)), __param(3, inject(SERVER_TYPES.MessengerNotificationService)), __param(4, inject(CommonType.MOLECULER_BROKER)), __param(5, inject('PubSub')), __param(6, inject(SERVER_TYPES.IRedisCacheManager)), __param(7, inject(CommonType.REDIS_CLIENT)), __param(8, inject('Logger')), __metadata("design:paramtypes", [PostRepository, ChannelRepository, Object, Object, ServiceBroker, PubSubEngine, Object, Function, Object])], PostService);export{PostService};//# sourceMappingURL=post-service.js.map
|