|
|
|
@ -20,6 +20,12 @@ const uuidv4 = require('uuid').v4;
|
|
|
|
|
|
|
|
|
|
const { SiteError, SiteService } = require('../../lib/site-lib');
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The entire concept of "get a user" is in flux right now. It's best to just
|
|
|
|
|
* ignore what's happening in this service right now, and focus on other
|
|
|
|
|
* features in the sytem.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
class UserService extends SiteService {
|
|
|
|
|
|
|
|
|
|
constructor (dtp) {
|
|
|
|
@ -102,25 +108,25 @@ class UserService extends SiteService {
|
|
|
|
|
user.password = maskedPassword;
|
|
|
|
|
|
|
|
|
|
user.flags = {
|
|
|
|
|
isAdmin: userDefinition.isAdmin || false,
|
|
|
|
|
isModerator: userDefinition.isModerator || false,
|
|
|
|
|
isEmailVerified: userDefinition.isEmailVerified || false,
|
|
|
|
|
isAdmin: false,
|
|
|
|
|
isModerator: false,
|
|
|
|
|
isEmailVerified: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
user.permissions = {
|
|
|
|
|
canLogin: userDefinition.canLogin || true,
|
|
|
|
|
canChat: userDefinition.canChat || true,
|
|
|
|
|
canComment: userDefinition.canComment || true,
|
|
|
|
|
canReport: userDefinition.canReport || true,
|
|
|
|
|
canAuthorPosts: userDefinition.canAuthorPosts || false,
|
|
|
|
|
canAuthorPages: userDefinition.canAuthorPages || false,
|
|
|
|
|
canPublishPosts: userDefinition.canPublishPosts || false,
|
|
|
|
|
canPublishPages: userDefinition.canPublishPages || false,
|
|
|
|
|
canLogin: true,
|
|
|
|
|
canChat: true,
|
|
|
|
|
canComment: true,
|
|
|
|
|
canReport: true,
|
|
|
|
|
canAuthorPosts: false,
|
|
|
|
|
canAuthorPages: false,
|
|
|
|
|
canPublishPosts: false,
|
|
|
|
|
canPublishPages: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
user.optIn = {
|
|
|
|
|
system: userDefinition.optInSystem || true,
|
|
|
|
|
marketing: userDefinition.optInMarketing || false,
|
|
|
|
|
system: true,
|
|
|
|
|
marketing: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.log.info('creating new user account', { email: userDefinition.email });
|
|
|
|
@ -184,7 +190,7 @@ class UserService extends SiteService {
|
|
|
|
|
|
|
|
|
|
async emailOptOut (userId, category) {
|
|
|
|
|
userId = mongoose.Types.ObjectId(userId);
|
|
|
|
|
const user = await this.getUserAccount(userId);
|
|
|
|
|
const user = await this.getLocalUserAccount(userId);
|
|
|
|
|
if (!user) {
|
|
|
|
|
throw new SiteError(406, 'Invalid opt-out token');
|
|
|
|
|
}
|
|
|
|
@ -209,7 +215,6 @@ class UserService extends SiteService {
|
|
|
|
|
throw SiteError(403, 'Invalid user account operation');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strip characters we don't want to allow in username
|
|
|
|
|
userDefinition.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, ''));
|
|
|
|
|
const username_lc = userDefinition.username.toLowerCase();
|
|
|
|
|
|
|
|
|
@ -231,8 +236,7 @@ class UserService extends SiteService {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async updateForAdmin (user, userDefinition) {
|
|
|
|
|
// strip characters we don't want to allow in username
|
|
|
|
|
async updateLocalForAdmin (user, userDefinition) {
|
|
|
|
|
userDefinition.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, ''));
|
|
|
|
|
const username_lc = userDefinition.username.toLowerCase();
|
|
|
|
|
|
|
|
|
@ -283,7 +287,6 @@ class UserService extends SiteService {
|
|
|
|
|
|
|
|
|
|
const updateOp = { $set: { }, $unset: { } };
|
|
|
|
|
|
|
|
|
|
// strip characters we don't want to allow in username
|
|
|
|
|
updateOp.$set.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, ''));
|
|
|
|
|
if (!updateOp.$set.username || (updateOp.$set.username.length === 0)) {
|
|
|
|
|
throw new SiteError(400, 'Must include a username');
|
|
|
|
@ -321,7 +324,7 @@ class UserService extends SiteService {
|
|
|
|
|
}, options);
|
|
|
|
|
|
|
|
|
|
const accountEmail = account.username.trim().toLowerCase();
|
|
|
|
|
const accountUsername = await this.filterUsername(accountEmail);
|
|
|
|
|
const accountUsername = this.filterUsername(accountEmail);
|
|
|
|
|
|
|
|
|
|
this.log.debug('locating user record', { accountEmail, accountUsername });
|
|
|
|
|
let user = await User
|
|
|
|
@ -469,28 +472,23 @@ class UserService extends SiteService {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filterUserObject (user) {
|
|
|
|
|
const filteredUser = {
|
|
|
|
|
_id: user._id,
|
|
|
|
|
created: user.created,
|
|
|
|
|
displayName: user.displayName,
|
|
|
|
|
username: user.username,
|
|
|
|
|
username_lc: user.username_lc,
|
|
|
|
|
bio: user.bio,
|
|
|
|
|
flags: user.flags,
|
|
|
|
|
permissions: user.permissions,
|
|
|
|
|
picture: user.picture,
|
|
|
|
|
};
|
|
|
|
|
if (filteredUser.flags && filteredUser.flags._id) {
|
|
|
|
|
delete filteredUser.flags._id;
|
|
|
|
|
async getLocalUserId (username) {
|
|
|
|
|
const user = await User.findOne({ username_lc: username }).select('_id').lean();
|
|
|
|
|
if (!user) {
|
|
|
|
|
return; // undefined
|
|
|
|
|
}
|
|
|
|
|
if (filteredUser.permissions && filteredUser.permissions._id) {
|
|
|
|
|
delete filteredUser.permissions._id;
|
|
|
|
|
return user._id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getCoreUserId (username) {
|
|
|
|
|
const user = await CoreUser.findOne({ username_lc: username }).select('_id').lean();
|
|
|
|
|
if (!user) {
|
|
|
|
|
return; // undefined
|
|
|
|
|
}
|
|
|
|
|
return filteredUser;
|
|
|
|
|
return user._id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getUserAccount (userId) {
|
|
|
|
|
async getLocalUserAccount (userId) {
|
|
|
|
|
const user = await User
|
|
|
|
|
.findById(userId)
|
|
|
|
|
.select('+email +flags +permissions +optIn +picture')
|
|
|
|
@ -512,11 +510,47 @@ class UserService extends SiteService {
|
|
|
|
|
user.hasAuthorDashboard = user.hasAuthorPermissions || user.hasPublishPermissions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getUserAccounts (pagination, username) {
|
|
|
|
|
async getCoreUserAccount (userId) {
|
|
|
|
|
const user = await User
|
|
|
|
|
.findById(userId)
|
|
|
|
|
.select('+email +flags +permissions +optIn +picture')
|
|
|
|
|
.populate(this.populateUser)
|
|
|
|
|
.lean();
|
|
|
|
|
if (!user) {
|
|
|
|
|
throw new SiteError(404, 'Core member account not found');
|
|
|
|
|
}
|
|
|
|
|
user.type = 'CoreUser';
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getLocalUserProfile (userId) {
|
|
|
|
|
const user = await User
|
|
|
|
|
.findById(userId)
|
|
|
|
|
.select('+email +flags +settings')
|
|
|
|
|
.populate(this.populateUser)
|
|
|
|
|
.lean();
|
|
|
|
|
user.type = 'User';
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getCoreUserProfile (userId) {
|
|
|
|
|
const user = await CoreUser
|
|
|
|
|
.findById(userId)
|
|
|
|
|
.select('+core +flags +settings')
|
|
|
|
|
.populate(this.populateUser)
|
|
|
|
|
.lean();
|
|
|
|
|
user.type = 'CoreUser';
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async searchLocalUserAccounts (pagination, username) {
|
|
|
|
|
let search = { };
|
|
|
|
|
|
|
|
|
|
if (username) {
|
|
|
|
|
username = this.filterUsername(username);
|
|
|
|
|
search.username_lc = { $regex: `^${username.toLowerCase().trim()}` };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const users = await User
|
|
|
|
|
.find(search)
|
|
|
|
|
.sort({ username_lc: 1 })
|
|
|
|
@ -529,60 +563,24 @@ class UserService extends SiteService {
|
|
|
|
|
return users.map((user) => { user.type = 'User'; return user; });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getUserProfile (userId) {
|
|
|
|
|
let user;
|
|
|
|
|
try {
|
|
|
|
|
userId = mongoose.Types.ObjectId(userId); // will throw if invalid format
|
|
|
|
|
user = User.findById(userId);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
user = User.findOne({ username: userId });
|
|
|
|
|
}
|
|
|
|
|
user = await user
|
|
|
|
|
.select('+email +flags +settings')
|
|
|
|
|
.populate(this.populateUser)
|
|
|
|
|
.lean();
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getPublicProfile (type, username) {
|
|
|
|
|
if (!username || (typeof username !== 'string')) {
|
|
|
|
|
throw new SiteError(406, 'Invalid username');
|
|
|
|
|
}
|
|
|
|
|
async searchCoreUserAccounts (pagination, username) {
|
|
|
|
|
let search = { };
|
|
|
|
|
|
|
|
|
|
username = username.trim().toLowerCase();
|
|
|
|
|
if (username.length === 0) {
|
|
|
|
|
throw new SiteError(406, 'Invalid username');
|
|
|
|
|
username = this.filterUsername(username);
|
|
|
|
|
if (username) {
|
|
|
|
|
search.username_lc = { $regex: `^${username.toLowerCase().trim()}` };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let user;
|
|
|
|
|
switch (type) {
|
|
|
|
|
case 'CoreUser':
|
|
|
|
|
user = await CoreUser
|
|
|
|
|
.findOne({ username_lc: username })
|
|
|
|
|
.select('_id created username username_lc displayName bio picture header core')
|
|
|
|
|
.populate(this.populateUser)
|
|
|
|
|
.lean();
|
|
|
|
|
if (user) {
|
|
|
|
|
user.type = 'CoreUser';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'User':
|
|
|
|
|
user = await User
|
|
|
|
|
.findOne({ username_lc: username })
|
|
|
|
|
.select('_id created username username_lc displayName bio picture header')
|
|
|
|
|
.populate(this.populateUser)
|
|
|
|
|
.lean();
|
|
|
|
|
if (user) {
|
|
|
|
|
user.type = 'User';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new SiteError(400, 'Invalid user account type');
|
|
|
|
|
}
|
|
|
|
|
const users = await CoreUser
|
|
|
|
|
.find(search)
|
|
|
|
|
.sort({ username_lc: 1 })
|
|
|
|
|
.select('+core +coreUserId +flags +permissions +optIn')
|
|
|
|
|
.skip(pagination.skip)
|
|
|
|
|
.limit(pagination.cpp)
|
|
|
|
|
.lean()
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
return user;
|
|
|
|
|
return users.map((user) => { user.type = 'CoreUser'; return user; });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getRecent (maxCount = 3) {
|
|
|
|
@ -664,10 +662,6 @@ class UserService extends SiteService {
|
|
|
|
|
return actions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async filterUsername (username) {
|
|
|
|
|
return striptags(username.trim().toLowerCase()).replace(/\W/g, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async checkUsername (username) {
|
|
|
|
|
if (!username || (typeof username !== 'string') || (username.length === 0)) {
|
|
|
|
|
throw new SiteError(406, 'Invalid username');
|
|
|
|
@ -683,6 +677,34 @@ class UserService extends SiteService {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filterUsername (username) {
|
|
|
|
|
while (username[0] === '@') {
|
|
|
|
|
username = username.slice(1);
|
|
|
|
|
}
|
|
|
|
|
return striptags(username.trim().toLowerCase()).replace(/\W/g, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filterUserObject (user) {
|
|
|
|
|
const filteredUser = {
|
|
|
|
|
_id: user._id,
|
|
|
|
|
created: user.created,
|
|
|
|
|
displayName: user.displayName,
|
|
|
|
|
username: user.username,
|
|
|
|
|
username_lc: user.username_lc,
|
|
|
|
|
bio: user.bio,
|
|
|
|
|
flags: user.flags,
|
|
|
|
|
permissions: user.permissions,
|
|
|
|
|
picture: user.picture,
|
|
|
|
|
};
|
|
|
|
|
if (filteredUser.flags && filteredUser.flags._id) {
|
|
|
|
|
delete filteredUser.flags._id;
|
|
|
|
|
}
|
|
|
|
|
if (filteredUser.permissions && filteredUser.permissions._id) {
|
|
|
|
|
delete filteredUser.permissions._id;
|
|
|
|
|
}
|
|
|
|
|
return filteredUser;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async recordProfileView (user, req) {
|
|
|
|
|
const { resource: resourceService } = this.dtp.services;
|
|
|
|
|
await resourceService.recordView(req, 'User', user._id);
|
|
|
|
@ -738,6 +760,41 @@ class UserService extends SiteService {
|
|
|
|
|
await User.updateOne({ _id: user._id }, { $unset: { 'picture': '' } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async updateHeaderImage (user, file) {
|
|
|
|
|
const { image: imageService } = this.dtp.services;
|
|
|
|
|
|
|
|
|
|
await this.removeHeaderImage(user.header);
|
|
|
|
|
|
|
|
|
|
const images = [
|
|
|
|
|
{
|
|
|
|
|
width: 1400,
|
|
|
|
|
height: 400,
|
|
|
|
|
format: 'jpeg',
|
|
|
|
|
formatParameters: {
|
|
|
|
|
quality: 80,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
await imageService.processImageFile(user, file, images);
|
|
|
|
|
await User.updateOne(
|
|
|
|
|
{ _id: user._id },
|
|
|
|
|
{
|
|
|
|
|
$set: {
|
|
|
|
|
'header': images[0].image._id,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async removeHeaderImage (user) {
|
|
|
|
|
const { image: imageService } = this.dtp.services;
|
|
|
|
|
user = await this.getUserAccount(user._id);
|
|
|
|
|
if (user.header) {
|
|
|
|
|
await imageService.deleteImage(user.header);
|
|
|
|
|
}
|
|
|
|
|
await User.updateOne({ _id: user._id }, { $unset: { 'header': '' } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async blockUser (user, blockedUser) {
|
|
|
|
|
if (user._id.equals(blockedUser._id)) {
|
|
|
|
|
throw new SiteError(406, "You can't block yourself");
|
|
|
|
@ -812,4 +869,4 @@ module.exports = {
|
|
|
|
|
slug: 'user',
|
|
|
|
|
name: 'user',
|
|
|
|
|
create: (dtp) => { return new UserService(dtp); },
|
|
|
|
|
};
|
|
|
|
|
};
|