You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
912 lines
17 KiB
912 lines
17 KiB
import { escapeRegExp } from '@rocket.chat/string-helpers';
|
|
|
|
import { BaseRaw } from './BaseRaw';
|
|
|
|
export class UsersRaw extends BaseRaw {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
this.defaultFields = {
|
|
__rooms: 0,
|
|
};
|
|
}
|
|
|
|
addRolesByUserId(uid, roles) {
|
|
if (!Array.isArray(roles)) {
|
|
roles = [roles];
|
|
process.env.NODE_ENV === 'development' && console.warn('[WARN] Users.addRolesByUserId: roles should be an array');
|
|
}
|
|
|
|
const query = {
|
|
_id: uid,
|
|
};
|
|
|
|
const update = {
|
|
$addToSet: {
|
|
roles: { $each: roles },
|
|
},
|
|
};
|
|
return this.updateOne(query, update);
|
|
}
|
|
|
|
findUsersInRoles(roles, scope, options) {
|
|
roles = [].concat(roles);
|
|
|
|
const query = {
|
|
roles: { $in: roles },
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findOneByUsername(username, options = null) {
|
|
const query = { username };
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findOneAgentById(_id, options) {
|
|
const query = {
|
|
_id,
|
|
roles: 'livechat-agent',
|
|
};
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findUsersInRolesWithQuery(roles, query, options) {
|
|
roles = [].concat(roles);
|
|
|
|
Object.assign(query, { roles: { $in: roles } });
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findOneByUsernameAndRoomIgnoringCase(username, rid, options) {
|
|
if (typeof username === 'string') {
|
|
username = new RegExp(`^${escapeRegExp(username)}$`, 'i');
|
|
}
|
|
|
|
const query = {
|
|
__rooms: rid,
|
|
username,
|
|
};
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findOneByIdAndLoginHashedToken(_id, token, options = {}) {
|
|
const query = {
|
|
_id,
|
|
'services.resume.loginTokens.hashedToken': token,
|
|
};
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findByActiveUsersExcept(searchTerm, exceptions, options, searchFields, extraQuery = [], { startsWith = false, endsWith = false } = {}) {
|
|
if (exceptions == null) {
|
|
exceptions = [];
|
|
}
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
if (!Array.isArray(exceptions)) {
|
|
exceptions = [exceptions];
|
|
}
|
|
|
|
// if the search term is empty, don't need to have the $or statement (because it would be an empty regex)
|
|
if (searchTerm === '') {
|
|
const query = {
|
|
$and: [
|
|
{
|
|
active: true,
|
|
username: { $exists: true, $nin: exceptions },
|
|
},
|
|
...extraQuery,
|
|
],
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
const termRegex = new RegExp((startsWith ? '^' : '') + escapeRegExp(searchTerm) + (endsWith ? '$' : ''), 'i');
|
|
|
|
// const searchFields = forcedSearchFields || settings.get('Accounts_SearchFields').trim().split(',');
|
|
|
|
const orStmt = (searchFields || []).reduce(function (acc, el) {
|
|
acc.push({ [el.trim()]: termRegex });
|
|
return acc;
|
|
}, []);
|
|
|
|
const query = {
|
|
$and: [
|
|
{
|
|
active: true,
|
|
username: { $exists: true, $nin: exceptions },
|
|
$or: orStmt,
|
|
},
|
|
...extraQuery,
|
|
],
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findActiveByIds(userIds, options = {}) {
|
|
const query = {
|
|
_id: { $in: userIds },
|
|
active: true,
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByIds(userIds, options = {}) {
|
|
const query = {
|
|
_id: { $in: userIds },
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findOneByUsernameIgnoringCase(username, options) {
|
|
if (typeof username === 'string') {
|
|
username = new RegExp(`^${escapeRegExp(username)}$`, 'i');
|
|
}
|
|
|
|
const query = { username };
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
async findOneByLDAPId(id, attribute = undefined) {
|
|
const query = {
|
|
'services.ldap.id': id,
|
|
};
|
|
|
|
if (attribute) {
|
|
query['services.ldap.idAttribute'] = attribute;
|
|
}
|
|
|
|
return this.findOne(query);
|
|
}
|
|
|
|
findLDAPUsers(options) {
|
|
const query = { ldap: true };
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findConnectedLDAPUsers(options) {
|
|
const query = {
|
|
'ldap': true,
|
|
'services.resume.loginTokens': {
|
|
$exists: true,
|
|
$ne: [],
|
|
},
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
isUserInRole(userId, roleName) {
|
|
const query = {
|
|
_id: userId,
|
|
roles: roleName,
|
|
};
|
|
|
|
return this.findOne(query, { projection: { roles: 1 } });
|
|
}
|
|
|
|
getDistinctFederationDomains() {
|
|
return this.col.distinct('federation.origin', { federation: { $exists: true } });
|
|
}
|
|
|
|
async getNextLeastBusyAgent(department, ignoreAgentId) {
|
|
const aggregate = [
|
|
{
|
|
$match: {
|
|
status: { $exists: true, $ne: 'offline' },
|
|
statusLivechat: 'available',
|
|
roles: 'livechat-agent',
|
|
...(ignoreAgentId && { _id: { $ne: ignoreAgentId } }),
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'rocketchat_subscription',
|
|
let: { id: '$_id' },
|
|
pipeline: [
|
|
{
|
|
$match: {
|
|
$expr: {
|
|
$and: [
|
|
{ $eq: ['$u._id', '$$id'] },
|
|
{ $eq: ['$open', true] },
|
|
{ $ne: ['$onHold', true] },
|
|
{ ...(department && { $eq: ['$department', department] }) },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
as: 'subs',
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'rocketchat_livechat_department_agents',
|
|
localField: '_id',
|
|
foreignField: 'agentId',
|
|
as: 'departments',
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
agentId: '$_id',
|
|
username: 1,
|
|
lastRoutingTime: 1,
|
|
departments: 1,
|
|
count: { $size: '$subs' },
|
|
},
|
|
},
|
|
{ $sort: { count: 1, lastRoutingTime: 1, username: 1 } },
|
|
];
|
|
|
|
if (department) {
|
|
aggregate.push({ $unwind: '$departments' });
|
|
aggregate.push({ $match: { 'departments.departmentId': department } });
|
|
}
|
|
|
|
aggregate.push({ $limit: 1 });
|
|
|
|
const [agent] = await this.col.aggregate(aggregate).toArray();
|
|
if (agent) {
|
|
await this.setLastRoutingTime(agent.agentId);
|
|
}
|
|
|
|
return agent;
|
|
}
|
|
|
|
async getLastAvailableAgentRouted(department, ignoreAgentId) {
|
|
const aggregate = [
|
|
{
|
|
$match: {
|
|
status: { $exists: true, $ne: 'offline' },
|
|
statusLivechat: 'available',
|
|
roles: 'livechat-agent',
|
|
...(ignoreAgentId && { _id: { $ne: ignoreAgentId } }),
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'rocketchat_livechat_department_agents',
|
|
localField: '_id',
|
|
foreignField: 'agentId',
|
|
as: 'departments',
|
|
},
|
|
},
|
|
{ $project: { agentId: '$_id', username: 1, lastRoutingTime: 1, departments: 1 } },
|
|
{ $sort: { lastRoutingTime: 1, username: 1 } },
|
|
];
|
|
|
|
if (department) {
|
|
aggregate.push({ $unwind: '$departments' });
|
|
aggregate.push({ $match: { 'departments.departmentId': department } });
|
|
}
|
|
|
|
aggregate.push({ $limit: 1 });
|
|
|
|
const [agent] = await this.col.aggregate(aggregate).toArray();
|
|
if (agent) {
|
|
await this.setLastRoutingTime(agent.agentId);
|
|
}
|
|
|
|
return agent;
|
|
}
|
|
|
|
async setLastRoutingTime(userId) {
|
|
const result = await this.col.findAndModify(
|
|
{ _id: userId },
|
|
{
|
|
sort: {
|
|
_id: 1,
|
|
},
|
|
},
|
|
{
|
|
$set: {
|
|
lastRoutingTime: new Date(),
|
|
},
|
|
},
|
|
);
|
|
return result.value;
|
|
}
|
|
|
|
setLivechatStatusIf(userId, status, conditions = {}, extraFields = {}) {
|
|
// TODO: Create class Agent
|
|
const query = {
|
|
_id: userId,
|
|
...conditions,
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
statusLivechat: status,
|
|
...extraFields,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
async getAgentAndAmountOngoingChats(userId) {
|
|
const aggregate = [
|
|
{
|
|
$match: {
|
|
_id: userId,
|
|
status: { $exists: true, $ne: 'offline' },
|
|
statusLivechat: 'available',
|
|
roles: 'livechat-agent',
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: 'rocketchat_subscription',
|
|
localField: '_id',
|
|
foreignField: 'u._id',
|
|
as: 'subs',
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
'agentId': '$_id',
|
|
'username': 1,
|
|
'lastAssignTime': 1,
|
|
'lastRoutingTime': 1,
|
|
'queueInfo.chats': {
|
|
$size: {
|
|
$filter: {
|
|
input: '$subs',
|
|
as: 'sub',
|
|
cond: {
|
|
$and: [{ $eq: ['$$sub.t', 'l'] }, { $eq: ['$$sub.open', true] }, { $ne: ['$$sub.onHold', true] }],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{ $sort: { 'queueInfo.chats': 1, 'lastAssignTime': 1, 'lastRoutingTime': 1, 'username': 1 } },
|
|
];
|
|
|
|
const [agent] = await this.col.aggregate(aggregate).toArray();
|
|
return agent;
|
|
}
|
|
|
|
findAllResumeTokensByUserId(userId) {
|
|
return this.col
|
|
.aggregate([
|
|
{
|
|
$match: {
|
|
_id: userId,
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
tokens: {
|
|
$filter: {
|
|
input: '$services.resume.loginTokens',
|
|
as: 'token',
|
|
cond: {
|
|
$ne: ['$$token.type', 'personalAccessToken'],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{ $unwind: '$tokens' },
|
|
{ $sort: { 'tokens.when': 1 } },
|
|
{ $group: { _id: '$_id', tokens: { $push: '$tokens' } } },
|
|
])
|
|
.toArray();
|
|
}
|
|
|
|
findActiveByUsernameOrNameRegexWithExceptionsAndConditions(termRegex, exceptions, conditions, options) {
|
|
if (exceptions == null) {
|
|
exceptions = [];
|
|
}
|
|
if (conditions == null) {
|
|
conditions = {};
|
|
}
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
if (!Array.isArray(exceptions)) {
|
|
exceptions = [exceptions];
|
|
}
|
|
|
|
const query = {
|
|
$or: [
|
|
{
|
|
username: termRegex,
|
|
},
|
|
{
|
|
name: termRegex,
|
|
},
|
|
{
|
|
nickname: termRegex,
|
|
},
|
|
],
|
|
active: true,
|
|
type: {
|
|
$in: ['user', 'bot'],
|
|
},
|
|
$and: [
|
|
{
|
|
username: {
|
|
$exists: true,
|
|
},
|
|
},
|
|
{
|
|
username: {
|
|
$nin: exceptions,
|
|
},
|
|
},
|
|
],
|
|
...conditions,
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
countAllAgentsStatus({ departmentId = undefined }) {
|
|
const match = {
|
|
$match: {
|
|
roles: { $in: ['livechat-agent'] },
|
|
},
|
|
};
|
|
const group = {
|
|
$group: {
|
|
_id: null,
|
|
offline: {
|
|
$sum: {
|
|
$cond: [
|
|
{
|
|
$or: [
|
|
{
|
|
$and: [{ $eq: ['$status', 'offline'] }, { $eq: ['$statusLivechat', 'available'] }],
|
|
},
|
|
{ $eq: ['$statusLivechat', 'not-available'] },
|
|
],
|
|
},
|
|
1,
|
|
0,
|
|
],
|
|
},
|
|
},
|
|
away: {
|
|
$sum: {
|
|
$cond: [
|
|
{
|
|
$and: [{ $eq: ['$status', 'away'] }, { $eq: ['$statusLivechat', 'available'] }],
|
|
},
|
|
1,
|
|
0,
|
|
],
|
|
},
|
|
},
|
|
busy: {
|
|
$sum: {
|
|
$cond: [
|
|
{
|
|
$and: [{ $eq: ['$status', 'busy'] }, { $eq: ['$statusLivechat', 'available'] }],
|
|
},
|
|
1,
|
|
0,
|
|
],
|
|
},
|
|
},
|
|
available: {
|
|
$sum: {
|
|
$cond: [
|
|
{
|
|
$and: [{ $eq: ['$status', 'online'] }, { $eq: ['$statusLivechat', 'available'] }],
|
|
},
|
|
1,
|
|
0,
|
|
],
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const lookup = {
|
|
$lookup: {
|
|
from: 'rocketchat_livechat_department_agents',
|
|
localField: '_id',
|
|
foreignField: 'agentId',
|
|
as: 'departments',
|
|
},
|
|
};
|
|
const unwind = {
|
|
$unwind: {
|
|
path: '$departments',
|
|
preserveNullAndEmptyArrays: true,
|
|
},
|
|
};
|
|
const departmentsMatch = {
|
|
$match: {
|
|
'departments.departmentId': departmentId,
|
|
},
|
|
};
|
|
const params = [match];
|
|
if (departmentId && departmentId !== 'undefined') {
|
|
params.push(lookup);
|
|
params.push(unwind);
|
|
params.push(departmentsMatch);
|
|
}
|
|
params.push(group);
|
|
return this.col.aggregate(params).toArray();
|
|
}
|
|
|
|
getTotalOfRegisteredUsersByDate({ start, end, options = {} }) {
|
|
const params = [
|
|
{
|
|
$match: {
|
|
createdAt: { $gte: start, $lte: end },
|
|
roles: { $ne: 'anonymous' },
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
$concat: [{ $substr: ['$createdAt', 0, 4] }, { $substr: ['$createdAt', 5, 2] }, { $substr: ['$createdAt', 8, 2] }],
|
|
},
|
|
users: { $sum: 1 },
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: '$_id',
|
|
users: { $sum: '$users' },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
date: '$_id',
|
|
users: 1,
|
|
type: 'users',
|
|
},
|
|
},
|
|
];
|
|
if (options.sort) {
|
|
params.push({ $sort: options.sort });
|
|
}
|
|
if (options.count) {
|
|
params.push({ $limit: options.count });
|
|
}
|
|
return this.col.aggregate(params).toArray();
|
|
}
|
|
|
|
getUserLanguages() {
|
|
const pipeline = [
|
|
{
|
|
$match: {
|
|
language: {
|
|
$exists: true,
|
|
$ne: '',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: '$language',
|
|
total: { $sum: 1 },
|
|
},
|
|
},
|
|
];
|
|
|
|
return this.col.aggregate(pipeline).toArray();
|
|
}
|
|
|
|
updateStatusText(_id, statusText) {
|
|
const update = {
|
|
$set: {
|
|
statusText,
|
|
},
|
|
};
|
|
|
|
return this.update({ _id }, update);
|
|
}
|
|
|
|
updateStatusByAppId(appId, status) {
|
|
const query = {
|
|
appId,
|
|
status: { $ne: status },
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
status,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
openAgentsBusinessHoursByBusinessHourId(businessHourIds) {
|
|
const query = {
|
|
roles: 'livechat-agent',
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
statusLivechat: 'available',
|
|
},
|
|
$addToSet: {
|
|
openBusinessHours: { $each: businessHourIds },
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
openAgentBusinessHoursByBusinessHourIdsAndAgentId(businessHourIds, agentId) {
|
|
const query = {
|
|
_id: agentId,
|
|
roles: 'livechat-agent',
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
statusLivechat: 'available',
|
|
},
|
|
$addToSet: {
|
|
openBusinessHours: { $each: businessHourIds },
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
addBusinessHourByAgentIds(agentIds = [], businessHourId) {
|
|
const query = {
|
|
_id: { $in: agentIds },
|
|
roles: 'livechat-agent',
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
statusLivechat: 'available',
|
|
},
|
|
$addToSet: {
|
|
openBusinessHours: businessHourId,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
removeBusinessHourByAgentIds(agentIds = [], businessHourId) {
|
|
const query = {
|
|
_id: { $in: agentIds },
|
|
roles: 'livechat-agent',
|
|
};
|
|
|
|
const update = {
|
|
$pull: {
|
|
openBusinessHours: businessHourId,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
openBusinessHourToAgentsWithoutDepartment(agentIdsWithDepartment = [], businessHourId) {
|
|
const query = {
|
|
_id: { $nin: agentIdsWithDepartment },
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
statusLivechat: 'available',
|
|
},
|
|
$addToSet: {
|
|
openBusinessHours: businessHourId,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
closeBusinessHourToAgentsWithoutDepartment(agentIdsWithDepartment = [], businessHourId) {
|
|
const query = {
|
|
_id: { $nin: agentIdsWithDepartment },
|
|
};
|
|
|
|
const update = {
|
|
$pull: {
|
|
openBusinessHours: businessHourId,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
closeAgentsBusinessHoursByBusinessHourIds(businessHourIds) {
|
|
const query = {
|
|
roles: 'livechat-agent',
|
|
};
|
|
|
|
const update = {
|
|
$pull: {
|
|
openBusinessHours: { $in: businessHourIds },
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
updateLivechatStatusBasedOnBusinessHours(userIds = []) {
|
|
const query = {
|
|
$or: [{ openBusinessHours: { $exists: false } }, { openBusinessHours: { $size: 0 } }],
|
|
roles: 'livechat-agent',
|
|
...(Array.isArray(userIds) && userIds.length > 0 && { _id: { $in: userIds } }),
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
statusLivechat: 'not-available',
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
setLivechatStatusActiveBasedOnBusinessHours(userId) {
|
|
const query = {
|
|
_id: userId,
|
|
openBusinessHours: {
|
|
$exists: true,
|
|
$not: { $size: 0 },
|
|
},
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
statusLivechat: 'available',
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
async isAgentWithinBusinessHours(agentId) {
|
|
return (
|
|
(await this.find({
|
|
_id: agentId,
|
|
openBusinessHours: {
|
|
$exists: true,
|
|
$not: { $size: 0 },
|
|
},
|
|
}).count()) > 0
|
|
);
|
|
}
|
|
|
|
removeBusinessHoursFromAllUsers() {
|
|
const query = {
|
|
roles: 'livechat-agent',
|
|
openBusinessHours: {
|
|
$exists: true,
|
|
},
|
|
};
|
|
|
|
const update = {
|
|
$unset: {
|
|
openBusinessHours: 1,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
resetTOTPById(userId) {
|
|
return this.col.updateOne(
|
|
{
|
|
_id: userId,
|
|
},
|
|
{
|
|
$unset: {
|
|
'services.totp': 1,
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
unsetLoginTokens(userId) {
|
|
return this.col.updateOne(
|
|
{
|
|
_id: userId,
|
|
},
|
|
{
|
|
$set: {
|
|
'services.resume.loginTokens': [],
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
removeNonPATLoginTokensExcept(userId, authToken) {
|
|
return this.col.updateOne(
|
|
{
|
|
_id: userId,
|
|
},
|
|
{
|
|
$pull: {
|
|
'services.resume.loginTokens': {
|
|
when: { $exists: true },
|
|
hashedToken: { $ne: authToken },
|
|
},
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
removeRoomsByRoomIdsAndUserId(rids, userId) {
|
|
return this.update(
|
|
{
|
|
_id: userId,
|
|
__rooms: { $in: rids },
|
|
},
|
|
{
|
|
$pullAll: { __rooms: rids },
|
|
},
|
|
{ multi: true },
|
|
);
|
|
}
|
|
|
|
removeRolesByUserId(uid, roles) {
|
|
const query = {
|
|
_id: uid,
|
|
};
|
|
|
|
const update = {
|
|
$pullAll: {
|
|
roles,
|
|
},
|
|
};
|
|
|
|
return this.updateOne(query, update);
|
|
}
|
|
|
|
async isUserInRoleScope(uid) {
|
|
const query = {
|
|
_id: uid,
|
|
};
|
|
|
|
const options = {
|
|
fields: { _id: 1 },
|
|
};
|
|
|
|
const found = await this.findOne(query, options);
|
|
return !!found;
|
|
}
|
|
|
|
addBannerById(_id, banner) {
|
|
const query = {
|
|
_id,
|
|
[`banners.${banner.id}.read`]: {
|
|
$ne: true,
|
|
},
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
[`banners.${banner.id}`]: banner,
|
|
},
|
|
};
|
|
|
|
return this.updateOne(query, update);
|
|
}
|
|
}
|
|
|