diff --git a/src/groups/join.js b/src/groups/join.js index 9fb02ec0bb..97f526e3a5 100644 --- a/src/groups/join.js +++ b/src/groups/join.js @@ -1,109 +1,206 @@ 'use strict'; -const winston = require('winston'); - -const db = require('../database'); -const user = require('../user'); -const plugins = require('../plugins'); -const cache = require('../cache'); - -module.exports = function (Groups) { - Groups.join = async function (groupNames, uid) { - if (!groupNames) { - throw new Error('[[error:invalid-data]]'); - } - if (Array.isArray(groupNames) && !groupNames.length) { - return; - } - if (!Array.isArray(groupNames)) { - groupNames = [groupNames]; - } - - if (!uid) { - throw new Error('[[error:invalid-uid]]'); - } - - const [isMembers, exists, isAdmin] = await Promise.all([ - Groups.isMemberOfGroups(uid, groupNames), - Groups.exists(groupNames), - user.isAdministrator(uid), - ]); - - const groupsToCreate = groupNames.filter((groupName, index) => groupName && !exists[index]); - const groupsToJoin = groupNames.filter((groupName, index) => !isMembers[index]); - - if (!groupsToJoin.length) { - return; - } - await createNonExistingGroups(groupsToCreate); - - const promises = [ - db.sortedSetsAdd(groupsToJoin.map(groupName => `group:${groupName}:members`), Date.now(), uid), - db.incrObjectField(groupsToJoin.map(groupName => `group:${groupName}`), 'memberCount'), - ]; - if (isAdmin) { - promises.push(db.setsAdd(groupsToJoin.map(groupName => `group:${groupName}:owners`), uid)); - } - - await Promise.all(promises); - - Groups.clearCache(uid, groupsToJoin); - cache.del(groupsToJoin.map(name => `group:${name}:members`)); - - const groupData = await Groups.getGroupsFields(groupsToJoin, ['name', 'hidden', 'memberCount']); - const visibleGroups = groupData.filter(groupData => groupData && !groupData.hidden); - - if (visibleGroups.length) { - await db.sortedSetAdd( - 'groups:visible:memberCount', - visibleGroups.map(groupData => groupData.memberCount), - visibleGroups.map(groupData => groupData.name) - ); +const __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P((resolve) => { resolve(value); }); } + const res = new (P || (P = Promise))((resolve, reject) => { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + return res; +}; +const __generator = (this && this.__generator) || function (thisArg, body) { + let f; let y; let t; let g; + let _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }; + return g = { next: verb(0), throw: verb(1), return: verb(2) }, typeof Symbol === 'function' && (g[Symbol.iterator] = function () { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError('Generator is already executing.'); + while (g && (g = 0, op[0] && (_ = 0)), _) { + try { + if (f = 1, y && (t = op[0] & 2 ? y.return : op[0] ? y.throw || ((t = y.return) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } } - - await setGroupTitleIfNotSet(groupsToJoin, uid); - - plugins.hooks.fire('action:group.join', { - groupNames: groupsToJoin, - uid: uid, + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.default = default_1; +/* eslint-disable import/no-import-module-exports */ +const winston = require('winston'); +/* eslint-disable import/no-import-module-exports */ +const database_1 = require('../database'); +/* eslint-disable import/no-import-module-exports */ +const user_1 = require('../user'); +/* eslint-disable import/no-import-module-exports */ +const plugins_1 = require('../plugins'); +/* eslint-disable import/no-import-module-exports */ +const cache_1 = require('../cache'); + +function default_1(Groups) { + Groups.join = function (groupNames, uid) { + return __awaiter(this, void 0, void 0, function () { + let _a; let isMembers; let exists; let isAdmin; let groupsToCreate; let groupsToJoin; let promises; let groupData; let + visibleGroups; + return __generator(this, (_b) => { + switch (_b.label) { + case 0: + if (!groupNames) { + throw new Error('[[error:invalid-data]]'); + } + if (Array.isArray(groupNames) && !groupNames.length) { + return [2]; + } + if (!Array.isArray(groupNames)) { + groupNames = [groupNames]; + } + if (!uid) { + throw new Error('[[error:invalid-uid]]'); + } + return [4 /* yield */, Promise.all([ + Groups.isMemberOfGroups(uid, groupNames), + Groups.exists(groupNames), + user_1.default.isAdministrator(uid), + ])]; + case 1: + _a = _b.sent(), isMembers = _a[0], exists = _a[1], isAdmin = _a[2]; + groupsToCreate = groupNames.filter((groupName, index) => groupName && !exists[index]); + groupsToJoin = groupNames.filter((groupName, index) => !isMembers[index]); + if (!groupsToJoin.length) { + return [2]; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + return [4 /* yield */, createNonExistingGroups(groupsToCreate)]; + case 2: + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + _b.sent(); + promises = [ + database_1.default.sortedSetsAdd(groupsToJoin.map(groupName => 'group:'.concat(groupName, ':members')), Date.now(), uid), + database_1.default.incrObjectField(groupsToJoin.map(groupName => 'group:'.concat(groupName)), 'memberCount'), + ]; + if (isAdmin) { + promises.push(database_1.default.setsAdd(groupsToJoin.map(groupName => 'group:'.concat(groupName, ':owners')), uid)); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + return [4 /* yield */, Promise.all(promises)]; + case 3: + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + _b.sent(); + Groups.clearCache(uid, groupsToJoin); + cache_1.default.del(groupsToJoin.map(name => 'group:'.concat(name, ':members'))); + return [4 /* yield */, Groups.getGroupsFields(groupsToJoin, ['name', 'hidden', 'memberCount'])]; + case 4: + groupData = _b.sent(); + visibleGroups = groupData.filter(groupData => groupData && !groupData.hidden); + if (!visibleGroups.length) return [3 /* break */, 6]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + return [4 /* yield */, database_1.default.sortedSetAdd('groups:visible:memberCount', visibleGroups.map(groupData => groupData.memberCount), visibleGroups.map(groupData => groupData.name))]; + case 5: + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + _b.sent(); + _b.label = 6; + case 6: + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + return [4 /* yield */, setGroupTitleIfNotSet(groupsToJoin, uid)]; + case 7: + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + _b.sent(); + plugins_1.default.hooks.fire('action:group.join', { + groupNames: groupsToJoin, + uid: uid, + }); + return [2]; + } + }); }); }; - - async function createNonExistingGroups(groupsToCreate) { - if (!groupsToCreate.length) { - return; - } - - for (const groupName of groupsToCreate) { - try { - // eslint-disable-next-line no-await-in-loop - await Groups.create({ - name: groupName, - hidden: 1, - }); - } catch (err) { - if (err && err.message !== '[[error:group-already-exists]]') { - winston.error(`[groups.join] Could not create new hidden group (${groupName})\n${err.stack}`); - throw err; + function createNonExistingGroups(groupsToCreate) { + return __awaiter(this, void 0, void 0, function () { + let _i; let groupsToCreate_1; let groupName; let + err_1; + return __generator(this, (_a) => { + switch (_a.label) { + case 0: + if (!groupsToCreate.length) { + return [2]; + } + _i = 0, groupsToCreate_1 = groupsToCreate; + _a.label = 1; + case 1: + if (!(_i < groupsToCreate_1.length)) return [3 /* break */, 6]; + groupName = groupsToCreate_1[_i]; + _a.label = 2; + case 2: + _a.trys.push([2, 4, , 5]); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + // eslint-disable-next-line no-await-in-loop + return [4 /* yield */, Groups.create({ + name: groupName, + hidden: 1, + })]; + case 3: + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + // eslint-disable-next-line no-await-in-loop + _a.sent(); + return [3 /* break */, 5]; + case 4: + err_1 = _a.sent(); + if (err_1 && err_1.message !== '[[error:group-already-exists]]') { + winston.error('[groups.join] Could not create new hidden group ('.concat(groupName, ')\n').concat(err_1.stack)); + throw err_1; + } + return [3 /* break */, 5]; + case 5: + _i++; + return [3 /* break */, 1]; + case 6: return [2]; } - } - } + }); + }); } - - async function setGroupTitleIfNotSet(groupNames, uid) { - const ignore = ['registered-users', 'verified-users', 'unverified-users', Groups.BANNED_USERS]; - groupNames = groupNames.filter( - groupName => !ignore.includes(groupName) && !Groups.isPrivilegeGroup(groupName) - ); - if (!groupNames.length) { - return; - } - - const currentTitle = await db.getObjectField(`user:${uid}`, 'groupTitle'); - if (currentTitle || currentTitle === '') { - return; - } - - await user.setUserField(uid, 'groupTitle', JSON.stringify(groupNames)); + function setGroupTitleIfNotSet(groupNames, uid) { + return __awaiter(this, void 0, void 0, function () { + let ignore; let + currentTitle; + return __generator(this, (_a) => { + switch (_a.label) { + case 0: + ignore = ['registered-users', 'verified-users', 'unverified-users', Groups.BANNED_USERS]; + groupNames = groupNames.filter(groupName => !ignore.includes(groupName) && !Groups.isPrivilegeGroup(groupName)); + if (!groupNames.length) { + return [2]; + } + return [4 /* yield */, database_1.default.getObjectField('user:'.concat(uid), 'groupTitle')]; + case 1: + currentTitle = _a.sent(); + if (currentTitle || currentTitle === '') { + return [2]; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + return [4 /* yield */, user_1.default.setUserField(uid, 'groupTitle', JSON.stringify(groupNames))]; + case 2: + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + _a.sent(); + return [2]; + } + }); + }); } -}; +} + diff --git a/src/groups/join.ts b/src/groups/join.ts new file mode 100644 index 0000000000..55126f123b --- /dev/null +++ b/src/groups/join.ts @@ -0,0 +1,116 @@ +'use strict'; + +/* eslint-disable import/no-import-module-exports */ +import winston = require ('winston'); +/* eslint-disable import/no-import-module-exports */ +import db from '../database'; +/* eslint-disable import/no-import-module-exports */ +import user from '../user'; +/* eslint-disable import/no-import-module-exports */ +import plugins from '../plugins'; +/* eslint-disable import/no-import-module-exports */ +import cache from '../cache'; + +export default function (Groups:any) { + Groups.join = async function (groupNames: String[] | String, uid: String): Promise { + if (!groupNames) { + throw new Error('[[error:invalid-data]]'); + } + if (Array.isArray(groupNames) && !groupNames.length) { + return; + } + if (!Array.isArray(groupNames)) { + groupNames = [groupNames]; + } + + if (!uid) { + throw new Error('[[error:invalid-uid]]'); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const [isMembers, exists, isAdmin] = await Promise.all([ + Groups.isMemberOfGroups(uid, groupNames), + Groups.exists(groupNames), + user.isAdministrator(uid), + ]); + + const groupsToCreate = groupNames.filter((groupName, index) => groupName && !exists[index]); + const groupsToJoin = groupNames.filter((groupName, index) => !isMembers[index]); + + if (!groupsToJoin.length) { + return; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await createNonExistingGroups(groupsToCreate); + + const promises = [ + db.sortedSetsAdd(groupsToJoin.map(groupName => `group:${groupName}:members`), Date.now(), uid), + db.incrObjectField(groupsToJoin.map(groupName => `group:${groupName}`), 'memberCount'), + ]; + if (isAdmin) { + promises.push(db.setsAdd(groupsToJoin.map(groupName => `group:${groupName}:owners`), uid)); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await Promise.all(promises); + + Groups.clearCache(uid, groupsToJoin); + cache.del(groupsToJoin.map(name => `group:${name}:members`)); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const groupData = await Groups.getGroupsFields(groupsToJoin, ['name', 'hidden', 'memberCount']); + const visibleGroups = groupData.filter((groupData: any) => groupData && !groupData.hidden); + + if (visibleGroups.length) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await db.sortedSetAdd( + 'groups:visible:memberCount', + visibleGroups.map((groupData: any) => groupData.memberCount), + visibleGroups.map((groupData: any) => groupData.name) + ); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await setGroupTitleIfNotSet(groupsToJoin, uid); + + plugins.hooks.fire('action:group.join', { + groupNames: groupsToJoin, + uid: uid, + }); + }; + + async function createNonExistingGroups(groupsToCreate: any): Promise { + if (!groupsToCreate.length) { + return; + } + + for (const groupName of groupsToCreate) { + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + // eslint-disable-next-line no-await-in-loop + await Groups.create({ + name: groupName, + hidden: 1, + }); + } catch (err) { + if (err && (err as {message: String}).message !== '[[error:group-already-exists]]') { + winston.error(`[groups.join] Could not create new hidden group (${groupName})\n${(err as {stack:String}).stack}`); + throw err; + } + } + } + } + + async function setGroupTitleIfNotSet(groupNames: String [], uid: String): Promise { + const ignore = ['registered-users', 'verified-users', 'unverified-users', Groups.BANNED_USERS]; + groupNames = groupNames.filter( + groupName => !ignore.includes(groupName) && !Groups.isPrivilegeGroup(groupName) + ); + if (!groupNames.length) { + return; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const currentTitle = await db.getObjectField(`user:${uid}`, 'groupTitle'); + if (currentTitle || currentTitle === '') { + return; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await user.setUserField(uid, 'groupTitle', JSON.stringify(groupNames)); + } +}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 10aeeef7e6..76c1287b0a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "allowJs": false, - "target": "es6", + "target": "es2017", "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true, @@ -14,4 +14,4 @@ "exclude":[ "node_modules", ] -} \ No newline at end of file +}