@yrpri/api 9.0.230 → 9.0.232

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 (68) hide show
  1. package/controllers/allOurIdeas.js +1 -1
  2. package/controllers/communities.cjs +282 -91
  3. package/controllers/domains.cjs +54 -8
  4. package/controllers/groups.cjs +51 -6
  5. package/controllers/points.cjs +4 -9
  6. package/controllers/posts.cjs +7 -9
  7. package/controllers/ratings.cjs +3 -6
  8. package/models/point.cjs +31 -3
  9. package/models/post.cjs +2 -3
  10. package/package.json +3 -3
  11. package/services/engine/allOurIdeas/aiHelper.d.ts +7 -4
  12. package/services/engine/allOurIdeas/aiHelper.js +34 -19
  13. package/services/engine/allOurIdeas/explainAnswersAssistant.d.ts +1 -1
  14. package/services/engine/allOurIdeas/explainAnswersAssistant.js +3 -9
  15. package/services/engine/moderation/fraud/CreateFraudAuditReport.cjs +35 -11
  16. package/services/engine/moderation/fraud/CreateFraudAuditReport.d.cts +21 -0
  17. package/services/engine/moderation/fraud/FraudBase.cjs +38 -18
  18. package/services/engine/moderation/fraud/FraudBase.d.cts +2 -0
  19. package/services/engine/moderation/fraud/FraudDeleteBase.cjs +48 -29
  20. package/services/engine/moderation/fraud/FraudDeleteBase.d.cts +8 -6
  21. package/services/engine/moderation/fraud/FraudDeleteEndorsements.cjs +5 -4
  22. package/services/engine/moderation/fraud/FraudDeleteEndorsements.d.cts +2 -2
  23. package/services/engine/moderation/fraud/FraudDeletePointQualities.cjs +3 -2
  24. package/services/engine/moderation/fraud/FraudDeletePointQualities.d.cts +1 -1
  25. package/services/engine/moderation/fraud/FraudDeletePoints.cjs +3 -2
  26. package/services/engine/moderation/fraud/FraudDeletePoints.d.cts +1 -1
  27. package/services/engine/moderation/fraud/FraudDeletePosts.cjs +3 -2
  28. package/services/engine/moderation/fraud/FraudDeleteRatings.cjs +61 -4
  29. package/services/engine/moderation/fraud/FraudGetBase.cjs +44 -20
  30. package/services/engine/moderation/fraud/FraudGetBase.d.cts +5 -0
  31. package/services/engine/moderation/fraud/FraudGetEndorsements.cjs +4 -13
  32. package/services/engine/moderation/fraud/FraudGetEndorsements.d.cts +1 -1
  33. package/services/engine/moderation/fraud/FraudGetPointQualities.cjs +3 -0
  34. package/services/engine/moderation/fraud/FraudGetPointQualities.d.cts +1 -1
  35. package/services/engine/moderation/fraud/FraudGetPoints.cjs +3 -0
  36. package/services/engine/moderation/fraud/FraudGetPoints.d.cts +1 -1
  37. package/services/engine/moderation/fraud/FraudGetPosts.cjs +17 -16
  38. package/services/engine/moderation/fraud/FraudGetPosts.d.cts +3 -3
  39. package/services/engine/moderation/fraud/FraudGetRatings.cjs +62 -30
  40. package/services/engine/moderation/fraud/FraudGetRatings.d.cts +4 -1
  41. package/services/engine/moderation/fraud/FraudRequestValidation.cjs +143 -0
  42. package/services/engine/moderation/fraud/FraudRequestValidation.d.cts +21 -0
  43. package/services/engine/moderation/fraud/FraudScannerNotifier.cjs +59 -35
  44. package/services/engine/moderation/fraud/FraudScannerNotifier.d.cts +20 -1
  45. package/services/llms/baseChatBot.d.ts +2 -0
  46. package/services/llms/baseChatBot.js +25 -9
  47. package/services/llms/imageGeneration/chatGptImageGenerator.d.ts +2 -2
  48. package/services/llms/imageGeneration/chatGptImageGenerator.js +13 -10
  49. package/services/llms/imageGeneration/collectionImageGenerator.js +31 -13
  50. package/services/llms/imageGeneration/dalleImageGenerator.d.ts +2 -2
  51. package/services/llms/imageGeneration/dalleImageGenerator.js +28 -16
  52. package/services/llms/imageGeneration/fluxImageGenerator.d.ts +2 -2
  53. package/services/llms/imageGeneration/fluxImageGenerator.js +9 -3
  54. package/services/llms/imageGeneration/iImageGenerator.d.ts +8 -1
  55. package/services/llms/imageGeneration/imageModelConfig.cjs +319 -0
  56. package/services/llms/imageGeneration/imageModelConfig.d.cts +79 -0
  57. package/services/llms/imageGeneration/imagenImageGenerator.d.ts +2 -3
  58. package/services/llms/imageGeneration/imagenImageGenerator.js +10 -10
  59. package/tests/fraudManagement.test.cjs +470 -0
  60. package/tests/fraudManagement.test.d.cts +1 -0
  61. package/tests/imageModelConfig.test.cjs +144 -0
  62. package/tests/imageModelConfig.test.d.cts +1 -0
  63. package/utils/ai_image_generation_guard.cjs +268 -0
  64. package/utils/ai_image_generation_guard.d.cts +34 -0
  65. package/utils/fingerprint_data.cjs +32 -0
  66. package/utils/fingerprint_data.d.cts +6 -0
  67. package/utils/recount_utils.cjs +53 -37
  68. package/utils/recount_utils.d.cts +7 -7
@@ -0,0 +1,268 @@
1
+ "use strict";
2
+ const { v4: uuidv4 } = require("uuid");
3
+ const models = require("../models/index.cjs");
4
+ const log = require("./logger.cjs");
5
+ const WINDOW_MS = 24 * 60 * 60 * 1000;
6
+ const WINDOW_TTL_SECONDS = 25 * 60 * 60;
7
+ const GENERATION_CONTEXTS = {
8
+ AOI_ICON_PUBLIC: "aoiIconPublic",
9
+ AOI_ICON_ADMIN: "aoiIconAdmin",
10
+ REGULAR_AI_IMAGE: "regularAiImage",
11
+ };
12
+ const parseLimit = (name, defaultValue) => {
13
+ const parsed = parseInt(process.env[name] || "", 10);
14
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultValue;
15
+ };
16
+ const getLimitForContext = (generationContext) => {
17
+ switch (generationContext) {
18
+ case GENERATION_CONTEXTS.AOI_ICON_PUBLIC:
19
+ return parseLimit("AI_IMAGE_PUBLIC_AOI_ICON_LIMIT_PER_24H", 10);
20
+ case GENERATION_CONTEXTS.AOI_ICON_ADMIN:
21
+ return parseLimit("AI_IMAGE_ADMIN_AOI_ICON_LIMIT_PER_24H", 150);
22
+ case GENERATION_CONTEXTS.REGULAR_AI_IMAGE:
23
+ return parseLimit("AI_IMAGE_REGULAR_LIMIT_PER_24H", 25);
24
+ default:
25
+ return undefined;
26
+ }
27
+ };
28
+ const deny = (status, body) => ({
29
+ allowed: false,
30
+ status,
31
+ body,
32
+ });
33
+ const isSameId = (a, b) => a !== undefined && a !== null && b !== undefined && b !== null && Number(a) === Number(b);
34
+ const hasGroupAdmin = async (group, user) => {
35
+ if (!group || !user)
36
+ return false;
37
+ if (isSameId(group.user_id, user.id))
38
+ return true;
39
+ return group.hasGroupAdmins ? await group.hasGroupAdmins(user) : false;
40
+ };
41
+ const hasCommunityAdmin = async (community, user) => {
42
+ if (!community || !user)
43
+ return false;
44
+ if (isSameId(community.user_id, user.id))
45
+ return true;
46
+ return community.hasCommunityAdmins
47
+ ? await community.hasCommunityAdmins(user)
48
+ : false;
49
+ };
50
+ const hasDomainAdmin = async (domain, user) => {
51
+ if (!domain || !user)
52
+ return false;
53
+ if (isSameId(domain.user_id, user.id))
54
+ return true;
55
+ return domain.hasDomainAdmins ? await domain.hasDomainAdmins(user) : false;
56
+ };
57
+ const loadCollection = async (collectionType, collectionId) => {
58
+ switch (collectionType) {
59
+ case "group":
60
+ return await models.Group.findOne({
61
+ where: { id: collectionId },
62
+ attributes: ["id", "user_id", "access", "configuration", "community_id"],
63
+ include: [
64
+ {
65
+ model: models.Community,
66
+ required: true,
67
+ attributes: ["id", "user_id", "access", "configuration", "domain_id"],
68
+ },
69
+ ],
70
+ });
71
+ case "community":
72
+ return await models.Community.findOne({
73
+ where: { id: collectionId },
74
+ attributes: [
75
+ "id",
76
+ "user_id",
77
+ "access",
78
+ "configuration",
79
+ "domain_id",
80
+ "only_admins_can_create_groups",
81
+ ],
82
+ });
83
+ case "domain":
84
+ return await models.Domain.findOne({
85
+ where: { id: collectionId },
86
+ attributes: [
87
+ "id",
88
+ "user_id",
89
+ "access",
90
+ "configuration",
91
+ "only_admins_can_create_communities",
92
+ ],
93
+ });
94
+ default:
95
+ return null;
96
+ }
97
+ };
98
+ const canGenerateForContext = async ({ collection, collectionType, generationContext, imageType, user, }) => {
99
+ if (!collection)
100
+ return false;
101
+ if (generationContext === GENERATION_CONTEXTS.AOI_ICON_PUBLIC ||
102
+ generationContext === GENERATION_CONTEXTS.AOI_ICON_ADMIN) {
103
+ if (imageType !== "icon")
104
+ return false;
105
+ }
106
+ else if (imageType === "icon") {
107
+ return false;
108
+ }
109
+ switch (generationContext) {
110
+ case GENERATION_CONTEXTS.AOI_ICON_PUBLIC:
111
+ return (collectionType === "group" &&
112
+ Boolean(collection.configuration?.allOurIdeas?.earl));
113
+ case GENERATION_CONTEXTS.AOI_ICON_ADMIN:
114
+ if (collectionType === "group")
115
+ return await hasGroupAdmin(collection, user);
116
+ if (collectionType === "community") {
117
+ return await hasCommunityAdmin(collection, user);
118
+ }
119
+ if (collectionType === "domain")
120
+ return await hasDomainAdmin(collection, user);
121
+ return false;
122
+ case GENERATION_CONTEXTS.REGULAR_AI_IMAGE:
123
+ if (collectionType === "group") {
124
+ return ((await hasGroupAdmin(collection, user)) ||
125
+ Boolean(collection.configuration?.allowGenerativeImages));
126
+ }
127
+ if (collectionType === "community") {
128
+ return await hasCommunityAdmin(collection, user);
129
+ }
130
+ if (collectionType === "domain")
131
+ return await hasDomainAdmin(collection, user);
132
+ return false;
133
+ default:
134
+ return false;
135
+ }
136
+ };
137
+ const rollingLimitScript = `
138
+ redis.call("ZREMRANGEBYSCORE", KEYS[1], "-inf", ARGV[1])
139
+ local count = redis.call("ZCARD", KEYS[1])
140
+ local limit = tonumber(ARGV[3])
141
+ if count >= limit then
142
+ local oldest = redis.call("ZRANGE", KEYS[1], 0, 0, "WITHSCORES")
143
+ local nextAllowedAt = 0
144
+ if oldest[2] then
145
+ nextAllowedAt = tonumber(oldest[2]) + tonumber(ARGV[5])
146
+ end
147
+ redis.call("EXPIRE", KEYS[1], tonumber(ARGV[6]))
148
+ return {0, count, nextAllowedAt}
149
+ end
150
+ redis.call("ZADD", KEYS[1], ARGV[2], ARGV[4])
151
+ redis.call("EXPIRE", KEYS[1], tonumber(ARGV[6]))
152
+ return {1, count + 1, 0}
153
+ `;
154
+ const checkAndConsumeRateLimit = async (redisClient, generationContext, userId) => {
155
+ const limit = getLimitForContext(generationContext);
156
+ if (!limit) {
157
+ return {
158
+ allowed: false,
159
+ status: 400,
160
+ body: { error: "invalid_generation_context" },
161
+ };
162
+ }
163
+ if (!redisClient || typeof redisClient.eval !== "function") {
164
+ log.error("Missing Redis client for AI image rate limit");
165
+ return {
166
+ allowed: false,
167
+ status: 503,
168
+ body: { error: "rate_limit_unavailable" },
169
+ };
170
+ }
171
+ const now = Date.now();
172
+ const oldestAllowed = now - WINDOW_MS;
173
+ const key = `rate:aiImage:${generationContext}:user:${userId}`;
174
+ const member = `${now}:${uuidv4()}`;
175
+ const result = await redisClient.eval(rollingLimitScript, {
176
+ keys: [key],
177
+ arguments: [
178
+ oldestAllowed.toString(),
179
+ now.toString(),
180
+ limit.toString(),
181
+ member,
182
+ WINDOW_MS.toString(),
183
+ WINDOW_TTL_SECONDS.toString(),
184
+ ],
185
+ });
186
+ const allowed = Number(result[0]) === 1;
187
+ const usedImagesInLast24Hours = Number(result[1]);
188
+ const nextAllowedAtMs = Number(result[2] || 0);
189
+ if (allowed) {
190
+ return {
191
+ allowed: true,
192
+ limit,
193
+ usedImagesInLast24Hours,
194
+ };
195
+ }
196
+ const retryAfterSeconds = Math.max(1, Math.ceil((nextAllowedAtMs - now) / 1000));
197
+ return {
198
+ allowed: false,
199
+ status: 429,
200
+ body: {
201
+ error: "rate_limited",
202
+ generationContext,
203
+ maxImagesPer24Hours: limit,
204
+ usedImagesInLast24Hours,
205
+ nextImageAllowedAt: new Date(nextAllowedAtMs).toISOString(),
206
+ retryAfterSeconds,
207
+ },
208
+ };
209
+ };
210
+ const validateImageGenerationStartRequest = async (req, { collectionType, collectionId }) => {
211
+ const numericCollectionId = Number(collectionId);
212
+ if (!Number.isInteger(numericCollectionId) || numericCollectionId <= 0) {
213
+ return deny(400, { error: "invalid_collection_id" });
214
+ }
215
+ if (!req.user) {
216
+ return deny(401, { error: "login_required" });
217
+ }
218
+ const generationContext = req.body?.generationContext;
219
+ if (!Object.values(GENERATION_CONTEXTS).includes(generationContext)) {
220
+ return deny(400, { error: "invalid_generation_context" });
221
+ }
222
+ const collection = await loadCollection(collectionType, numericCollectionId);
223
+ if (!collection) {
224
+ return deny(404, { error: "collection_not_found" });
225
+ }
226
+ const canGenerate = await canGenerateForContext({
227
+ collection,
228
+ collectionType,
229
+ generationContext,
230
+ imageType: req.body?.imageType,
231
+ user: req.user,
232
+ });
233
+ if (!canGenerate) {
234
+ return deny(403, { error: "image_generation_not_allowed" });
235
+ }
236
+ const rateLimit = await checkAndConsumeRateLimit(req.redisClient, generationContext, req.user.id);
237
+ if (!rateLimit.allowed) {
238
+ return deny(rateLimit.status, rateLimit.body);
239
+ }
240
+ return {
241
+ allowed: true,
242
+ userId: req.user.id,
243
+ generationContext,
244
+ internalData: {
245
+ userId: req.user.id,
246
+ generationContext,
247
+ imageType: req.body?.imageType,
248
+ collectionType,
249
+ collectionId: numericCollectionId,
250
+ createdAt: new Date().toISOString(),
251
+ maxImagesPer24Hours: rateLimit.limit,
252
+ usedImagesInLast24Hours: rateLimit.usedImagesInLast24Hours,
253
+ },
254
+ };
255
+ };
256
+ const canPollImageGenerationJob = (req, job) => Boolean(req.user && job?.internal_data?.userId === req.user.id);
257
+ const publicJobFields = (job) => ({
258
+ id: job.id,
259
+ progress: job.progress,
260
+ error: job.error,
261
+ data: job.data,
262
+ });
263
+ module.exports = {
264
+ GENERATION_CONTEXTS,
265
+ validateImageGenerationStartRequest,
266
+ canPollImageGenerationJob,
267
+ publicJobFields,
268
+ };
@@ -0,0 +1,34 @@
1
+ export namespace GENERATION_CONTEXTS {
2
+ let AOI_ICON_PUBLIC: string;
3
+ let AOI_ICON_ADMIN: string;
4
+ let REGULAR_AI_IMAGE: string;
5
+ }
6
+ export function validateImageGenerationStartRequest(req: any, { collectionType, collectionId }: {
7
+ collectionType: any;
8
+ collectionId: any;
9
+ }): Promise<{
10
+ allowed: boolean;
11
+ status: any;
12
+ body: any;
13
+ } | {
14
+ allowed: boolean;
15
+ userId: any;
16
+ generationContext: any;
17
+ internalData: {
18
+ userId: any;
19
+ generationContext: any;
20
+ imageType: any;
21
+ collectionType: any;
22
+ collectionId: number;
23
+ createdAt: string;
24
+ maxImagesPer24Hours: any;
25
+ usedImagesInLast24Hours: number | undefined;
26
+ };
27
+ }>;
28
+ export function canPollImageGenerationJob(req: any, job: any): boolean;
29
+ export function publicJobFields(job: any): {
30
+ id: any;
31
+ progress: any;
32
+ error: any;
33
+ data: any;
34
+ };
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ const INVALID_FINGERPRINT_VALUES = new Set(["", "undefined", "null"]);
3
+ const normalizeFingerprintValue = (value) => {
4
+ if (value === undefined || value === null) {
5
+ return undefined;
6
+ }
7
+ const normalizedValue = String(value).trim();
8
+ if (INVALID_FINGERPRINT_VALUES.has(normalizedValue.toLowerCase())) {
9
+ return undefined;
10
+ }
11
+ return normalizedValue;
12
+ };
13
+ const normalizeFingerprintConfidence = (value) => {
14
+ const normalizedValue = normalizeFingerprintValue(value);
15
+ const parsedValue = Number(normalizedValue);
16
+ return Number.isFinite(parsedValue) ? parsedValue : 0;
17
+ };
18
+ const getFingerprintDataFromBody = (body, prefix) => {
19
+ const requestBody = body || {};
20
+ const baseId = normalizeFingerprintValue(requestBody[`${prefix}BaseId`]);
21
+ const valCode = normalizeFingerprintValue(requestBody[`${prefix}ValCode`]);
22
+ const confidence = normalizeFingerprintConfidence(requestBody[`${prefix}Conf`]);
23
+ return {
24
+ browserId: baseId || valCode,
25
+ browserFingerprint: valCode,
26
+ browserFingerprintConfidence: confidence,
27
+ };
28
+ };
29
+ module.exports = {
30
+ getFingerprintDataFromBody,
31
+ normalizeFingerprintValue,
32
+ };
@@ -0,0 +1,6 @@
1
+ export function getFingerprintDataFromBody(body: any, prefix: any): {
2
+ browserId: string | undefined;
3
+ browserFingerprint: string | undefined;
4
+ browserFingerprintConfidence: number;
5
+ };
6
+ export function normalizeFingerprintValue(value: any): string | undefined;
@@ -5,21 +5,21 @@ const _ = require('lodash');
5
5
  const fs = require('fs');
6
6
  const request = require("./requestCompat.cjs");
7
7
  const log = require('./logger.cjs');
8
- const recountPosts = (postIds, done) => {
8
+ const recountPosts = (postIds, done, transaction) => {
9
9
  async.forEachSeries(postIds, (postId, forEachPostCallback) => {
10
- recountPost(postId, forEachPostCallback);
10
+ recountPost(postId, forEachPostCallback, transaction);
11
11
  }, error => {
12
12
  done(error);
13
13
  });
14
14
  };
15
- const recountPoints = (pointIds, done) => {
15
+ const recountPoints = (pointIds, done, transaction) => {
16
16
  async.forEachSeries(pointIds, (pointId, forEachPostCallback) => {
17
- recountPoint(pointId, forEachPostCallback);
17
+ recountPoint(pointId, forEachPostCallback, transaction);
18
18
  }, error => {
19
19
  done(error);
20
20
  });
21
21
  };
22
- const recountPost = (postId, done) => {
22
+ const recountPost = (postId, done, transaction) => {
23
23
  var endorsementsCount;
24
24
  var oppositionCount;
25
25
  var pointCount;
@@ -29,7 +29,8 @@ const recountPost = (postId, done) => {
29
29
  where: {
30
30
  value: 1,
31
31
  post_id: postId
32
- }
32
+ },
33
+ transaction
33
34
  }).then((count) => {
34
35
  endorsementsCount = count;
35
36
  parallelCallback();
@@ -42,7 +43,8 @@ const recountPost = (postId, done) => {
42
43
  where: {
43
44
  value: -1,
44
45
  post_id: postId
45
- }
46
+ },
47
+ transaction
46
48
  }).then((count) => {
47
49
  oppositionCount = count;
48
50
  parallelCallback();
@@ -58,7 +60,8 @@ const recountPost = (postId, done) => {
58
60
  { value: 1 }
59
61
  ],
60
62
  post_id: postId
61
- }
63
+ },
64
+ transaction
62
65
  }).then((count) => {
63
66
  pointCount = count;
64
67
  parallelCallback();
@@ -75,12 +78,13 @@ const recountPost = (postId, done) => {
75
78
  where: {
76
79
  id: postId
77
80
  },
78
- attributes: ['id', 'counter_endorsements_up', 'counter_endorsements_down', 'counter_points']
81
+ attributes: ['id', 'counter_endorsements_up', 'counter_endorsements_down', 'counter_points'],
82
+ transaction
79
83
  }).then((post) => {
80
84
  post.counter_points = pointCount;
81
85
  post.counter_endorsements_up = endorsementsCount;
82
86
  post.counter_endorsements_down = oppositionCount;
83
- post.save().then((results) => {
87
+ post.save({ transaction }).then((results) => {
84
88
  log.info(`Recount for post ${post.id} done`);
85
89
  done();
86
90
  });
@@ -90,7 +94,7 @@ const recountPost = (postId, done) => {
90
94
  }
91
95
  });
92
96
  };
93
- const recountPoint = (pointId, done) => {
97
+ const recountPoint = (pointId, done, transaction) => {
94
98
  var counter_quality_up;
95
99
  var counter_quality_down;
96
100
  var pointCount;
@@ -100,7 +104,8 @@ const recountPoint = (pointId, done) => {
100
104
  where: {
101
105
  value: 1,
102
106
  point_id: pointId
103
- }
107
+ },
108
+ transaction
104
109
  }).then((count) => {
105
110
  counter_quality_up = count;
106
111
  parallelCallback();
@@ -113,7 +118,8 @@ const recountPoint = (pointId, done) => {
113
118
  where: {
114
119
  value: -1,
115
120
  point_id: pointId
116
- }
121
+ },
122
+ transaction
117
123
  }).then((count) => {
118
124
  counter_quality_down = count;
119
125
  parallelCallback();
@@ -130,11 +136,12 @@ const recountPoint = (pointId, done) => {
130
136
  where: {
131
137
  id: pointId
132
138
  },
133
- attributes: ['id', 'counter_quality_up', 'counter_quality_down']
139
+ attributes: ['id', 'counter_quality_up', 'counter_quality_down'],
140
+ transaction
134
141
  }).then((point) => {
135
142
  point.counter_quality_up = counter_quality_up;
136
143
  point.counter_quality_down = counter_quality_down;
137
- point.save().then((results) => {
144
+ point.save({ transaction }).then((results) => {
138
145
  log.info(`Recount for point ${point.id} done`);
139
146
  done();
140
147
  });
@@ -144,29 +151,31 @@ const recountPoint = (pointId, done) => {
144
151
  }
145
152
  });
146
153
  };
147
- const countPostInGroup = (groupId, done) => {
154
+ const countPostInGroup = (groupId, done, transaction) => {
148
155
  models.Post.count({
149
156
  where: {
150
157
  group_id: groupId
151
- }
158
+ },
159
+ transaction
152
160
  }).then(count => {
153
161
  done(null, count);
154
162
  }).catch(error => {
155
163
  done(error);
156
164
  });
157
165
  };
158
- const countPointsInGroup = (groupId, done) => {
166
+ const countPointsInGroup = (groupId, done, transaction) => {
159
167
  models.Point.count({
160
168
  where: {
161
169
  group_id: groupId
162
- }
170
+ },
171
+ transaction
163
172
  }).then(count => {
164
173
  done(null, count);
165
174
  }).catch(error => {
166
175
  done(error);
167
176
  });
168
177
  };
169
- const countUsersInGroup = (groupId, done) => {
178
+ const countUsersInGroup = (groupId, done, transaction) => {
170
179
  const userIds = [];
171
180
  async.series([
172
181
  (seriesCallback) => {
@@ -180,7 +189,8 @@ const countUsersInGroup = (groupId, done) => {
180
189
  group_id: groupId
181
190
  }
182
191
  }
183
- ]
192
+ ],
193
+ transaction
184
194
  }).then(endorsements => {
185
195
  for (let i = 0; i < endorsements.length; i++) {
186
196
  userIds.push(endorsements[i].user_id);
@@ -199,7 +209,8 @@ const countUsersInGroup = (groupId, done) => {
199
209
  group_id: groupId
200
210
  }
201
211
  }
202
- ]
212
+ ],
213
+ transaction
203
214
  }).then(ratings => {
204
215
  for (let i = 0; i < ratings.length; i++) {
205
216
  userIds.push(ratings[i].user_id);
@@ -224,7 +235,8 @@ const countUsersInGroup = (groupId, done) => {
224
235
  }
225
236
  ]
226
237
  }
227
- ]
238
+ ],
239
+ transaction
228
240
  }).then(qualities => {
229
241
  for (let i = 0; i < qualities.length; i++) {
230
242
  userIds.push(qualities[i].user_id);
@@ -243,7 +255,8 @@ const countUsersInGroup = (groupId, done) => {
243
255
  group_id: groupId
244
256
  }
245
257
  }
246
- ]
258
+ ],
259
+ transaction
247
260
  }).then(points => {
248
261
  for (let i = 0; i < points.length; i++) {
249
262
  userIds.push(points[i].user_id);
@@ -256,7 +269,8 @@ const countUsersInGroup = (groupId, done) => {
256
269
  attributes: ['id'],
257
270
  where: {
258
271
  group_id: groupId
259
- }
272
+ },
273
+ transaction
260
274
  }).then(posts => {
261
275
  for (let i = 0; i < posts.length; i++) {
262
276
  userIds.push(posts[i].user_id);
@@ -268,7 +282,7 @@ const countUsersInGroup = (groupId, done) => {
268
282
  done(error, _.uniq(userIds).length, userIds);
269
283
  });
270
284
  };
271
- const countAllInGroup = (groupId, done) => {
285
+ const countAllInGroup = (groupId, done, transaction) => {
272
286
  let postCount = 0;
273
287
  let pointCount = 0;
274
288
  let userCount = 0;
@@ -283,7 +297,7 @@ const countAllInGroup = (groupId, done) => {
283
297
  postCount = count;
284
298
  seriesCallback();
285
299
  }
286
- });
300
+ }, transaction);
287
301
  },
288
302
  (seriesCallback) => {
289
303
  countPointsInGroup(groupId, (error, count) => {
@@ -294,7 +308,7 @@ const countAllInGroup = (groupId, done) => {
294
308
  pointCount = count;
295
309
  seriesCallback();
296
310
  }
297
- });
311
+ }, transaction);
298
312
  },
299
313
  (seriesCallback) => {
300
314
  countUsersInGroup(groupId, (error, count, allUsersIn) => {
@@ -306,7 +320,7 @@ const countAllInGroup = (groupId, done) => {
306
320
  allUsers = allUsersIn;
307
321
  seriesCallback();
308
322
  }
309
- });
323
+ }, transaction);
310
324
  }
311
325
  ], error => {
312
326
  if (error) {
@@ -317,11 +331,12 @@ const countAllInGroup = (groupId, done) => {
317
331
  }
318
332
  });
319
333
  };
320
- const recountGroup = (groupId, callback) => {
334
+ const recountGroup = (groupId, callback, transaction) => {
321
335
  models.Group.findOne({
322
336
  where: {
323
337
  id: groupId
324
- }
338
+ },
339
+ transaction
325
340
  }).then(group => {
326
341
  if (group) {
327
342
  countAllInGroup(group.id, (error, postCount, pointCount, userCount, allUsers) => {
@@ -332,14 +347,14 @@ const recountGroup = (groupId, callback) => {
332
347
  group.set('counter_points', pointCount);
333
348
  group.set('counter_users', Math.max(userCount, 1));
334
349
  group.set('counter_posts', postCount);
335
- group.save().then(() => {
350
+ group.save({ transaction }).then(() => {
336
351
  log.info(`Recount Group ${group.id} users: ${Math.max(userCount, 1)} posts: ${postCount} points: ${pointCount}`);
337
352
  callback(null, { postCount, pointCount, userCount, allUsers });
338
353
  }).catch(error => {
339
354
  callback(error);
340
355
  });
341
356
  }
342
- });
357
+ }, transaction);
343
358
  }
344
359
  else {
345
360
  callback("Group not found");
@@ -348,7 +363,7 @@ const recountGroup = (groupId, callback) => {
348
363
  callback(error);
349
364
  });
350
365
  };
351
- const recountCommunity = (communityId, callback) => {
366
+ const recountCommunity = (communityId, callback, transaction) => {
352
367
  models.Community.findOne({
353
368
  where: {
354
369
  id: communityId
@@ -362,7 +377,8 @@ const recountCommunity = (communityId, callback) => {
362
377
  is_group_folder: false
363
378
  }
364
379
  }
365
- ]
380
+ ],
381
+ transaction
366
382
  }).then(community => {
367
383
  if (community) {
368
384
  let postCount = 0;
@@ -379,7 +395,7 @@ const recountCommunity = (communityId, callback) => {
379
395
  allUsers = allUsers.concat(counts.allUsers);
380
396
  forEachCallback();
381
397
  }
382
- });
398
+ }, transaction);
383
399
  }, error => {
384
400
  if (error) {
385
401
  callback(error);
@@ -388,7 +404,7 @@ const recountCommunity = (communityId, callback) => {
388
404
  community.set('counter_points', pointCount);
389
405
  community.set('counter_users', Math.max(_.uniq(allUsers).length, 1));
390
406
  community.set('counter_posts', postCount);
391
- community.save().then(() => {
407
+ community.save({ transaction }).then(() => {
392
408
  log.info(`Recount Community ${community.id} users: ${Math.max(_.uniq(allUsers).length, 1)} posts: ${postCount} points: ${pointCount}`);
393
409
  callback();
394
410
  }).catch(error => {
@@ -1,7 +1,7 @@
1
- export function recountCommunity(communityId: any, callback: any): void;
2
- export function recountGroup(groupId: any, callback: any): void;
3
- export function recountPost(postId: any, done: any): void;
4
- export function recountPosts(postIds: any, done: any): void;
5
- export function recountPoint(pointId: any, done: any): void;
6
- export function recountPoints(pointIds: any, done: any): void;
7
- export function countUsersInGroup(groupId: any, done: any): void;
1
+ export function recountCommunity(communityId: any, callback: any, transaction: any): void;
2
+ export function recountGroup(groupId: any, callback: any, transaction: any): void;
3
+ export function recountPost(postId: any, done: any, transaction: any): void;
4
+ export function recountPosts(postIds: any, done: any, transaction: any): void;
5
+ export function recountPoint(pointId: any, done: any, transaction: any): void;
6
+ export function recountPoints(pointIds: any, done: any, transaction: any): void;
7
+ export function countUsersInGroup(groupId: any, done: any, transaction: any): void;