@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.
- package/controllers/allOurIdeas.js +1 -1
- package/controllers/communities.cjs +282 -91
- package/controllers/domains.cjs +54 -8
- package/controllers/groups.cjs +51 -6
- package/controllers/points.cjs +4 -9
- package/controllers/posts.cjs +7 -9
- package/controllers/ratings.cjs +3 -6
- package/models/point.cjs +31 -3
- package/models/post.cjs +2 -3
- package/package.json +3 -3
- package/services/engine/allOurIdeas/aiHelper.d.ts +7 -4
- package/services/engine/allOurIdeas/aiHelper.js +34 -19
- package/services/engine/allOurIdeas/explainAnswersAssistant.d.ts +1 -1
- package/services/engine/allOurIdeas/explainAnswersAssistant.js +3 -9
- package/services/engine/moderation/fraud/CreateFraudAuditReport.cjs +35 -11
- package/services/engine/moderation/fraud/CreateFraudAuditReport.d.cts +21 -0
- package/services/engine/moderation/fraud/FraudBase.cjs +38 -18
- package/services/engine/moderation/fraud/FraudBase.d.cts +2 -0
- package/services/engine/moderation/fraud/FraudDeleteBase.cjs +48 -29
- package/services/engine/moderation/fraud/FraudDeleteBase.d.cts +8 -6
- package/services/engine/moderation/fraud/FraudDeleteEndorsements.cjs +5 -4
- package/services/engine/moderation/fraud/FraudDeleteEndorsements.d.cts +2 -2
- package/services/engine/moderation/fraud/FraudDeletePointQualities.cjs +3 -2
- package/services/engine/moderation/fraud/FraudDeletePointQualities.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudDeletePoints.cjs +3 -2
- package/services/engine/moderation/fraud/FraudDeletePoints.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudDeletePosts.cjs +3 -2
- package/services/engine/moderation/fraud/FraudDeleteRatings.cjs +61 -4
- package/services/engine/moderation/fraud/FraudGetBase.cjs +44 -20
- package/services/engine/moderation/fraud/FraudGetBase.d.cts +5 -0
- package/services/engine/moderation/fraud/FraudGetEndorsements.cjs +4 -13
- package/services/engine/moderation/fraud/FraudGetEndorsements.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudGetPointQualities.cjs +3 -0
- package/services/engine/moderation/fraud/FraudGetPointQualities.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudGetPoints.cjs +3 -0
- package/services/engine/moderation/fraud/FraudGetPoints.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudGetPosts.cjs +17 -16
- package/services/engine/moderation/fraud/FraudGetPosts.d.cts +3 -3
- package/services/engine/moderation/fraud/FraudGetRatings.cjs +62 -30
- package/services/engine/moderation/fraud/FraudGetRatings.d.cts +4 -1
- package/services/engine/moderation/fraud/FraudRequestValidation.cjs +143 -0
- package/services/engine/moderation/fraud/FraudRequestValidation.d.cts +21 -0
- package/services/engine/moderation/fraud/FraudScannerNotifier.cjs +59 -35
- package/services/engine/moderation/fraud/FraudScannerNotifier.d.cts +20 -1
- package/services/llms/baseChatBot.d.ts +2 -0
- package/services/llms/baseChatBot.js +25 -9
- package/services/llms/imageGeneration/chatGptImageGenerator.d.ts +2 -2
- package/services/llms/imageGeneration/chatGptImageGenerator.js +13 -10
- package/services/llms/imageGeneration/collectionImageGenerator.js +31 -13
- package/services/llms/imageGeneration/dalleImageGenerator.d.ts +2 -2
- package/services/llms/imageGeneration/dalleImageGenerator.js +28 -16
- package/services/llms/imageGeneration/fluxImageGenerator.d.ts +2 -2
- package/services/llms/imageGeneration/fluxImageGenerator.js +9 -3
- package/services/llms/imageGeneration/iImageGenerator.d.ts +8 -1
- package/services/llms/imageGeneration/imageModelConfig.cjs +319 -0
- package/services/llms/imageGeneration/imageModelConfig.d.cts +79 -0
- package/services/llms/imageGeneration/imagenImageGenerator.d.ts +2 -3
- package/services/llms/imageGeneration/imagenImageGenerator.js +10 -10
- package/tests/fraudManagement.test.cjs +470 -0
- package/tests/fraudManagement.test.d.cts +1 -0
- package/tests/imageModelConfig.test.cjs +144 -0
- package/tests/imageModelConfig.test.d.cts +1 -0
- package/utils/ai_image_generation_guard.cjs +268 -0
- package/utils/ai_image_generation_guard.d.cts +34 -0
- package/utils/fingerprint_data.cjs +32 -0
- package/utils/fingerprint_data.d.cts +6 -0
- package/utils/recount_utils.cjs +53 -37
- 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
|
+
};
|
package/utils/recount_utils.cjs
CHANGED
|
@@ -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;
|