mirror of
https://github.com/wassname/talk.git
synced 2026-06-30 09:58:52 +08:00
added user query by value to graph
This commit is contained in:
@@ -27,7 +27,14 @@ const CONFIG = {
|
||||
// WEBPACK indicates when webpack is currently building.
|
||||
WEBPACK: process.env.WEBPACK === 'TRUE',
|
||||
|
||||
// APOLLO_ENGINE_KEY specifies the key used to connect Talk to
|
||||
// https://engine.apollo.com/ for tracing of GraphQL requests.
|
||||
//
|
||||
// Note: Apollo Engine is a premium service, may not be free for certain
|
||||
// volumes of queries.
|
||||
APOLLO_ENGINE_KEY: process.env.APOLLO_ENGINE_KEY || null,
|
||||
|
||||
// ENABLE_TRACING is true when the APOLLO_ENGINE_KEY is provided.
|
||||
ENABLE_TRACING: Boolean(process.env.APOLLO_ENGINE_KEY),
|
||||
|
||||
// EMAIL_SUBJECT_PREFIX is the string before emails in the subject.
|
||||
|
||||
+38
-10
@@ -8,6 +8,7 @@ const {
|
||||
} = require('../../perms/constants');
|
||||
|
||||
const UsersService = require('../../services/users');
|
||||
const {escapeRegExp} = require('../../services/regex');
|
||||
const UserModel = require('../../models/user');
|
||||
|
||||
const mergeState = (query, state) => {
|
||||
@@ -72,14 +73,48 @@ const genUserByIDs = async (context, ids) => {
|
||||
* @param {Object} context graph context
|
||||
* @param {Object} query query terms to apply to the users query
|
||||
*/
|
||||
const getUsersByQuery = async ({user}, {ids, limit, cursor, state, action_type, sortOrder}) => {
|
||||
const getUsersByQuery = async ({user}, {limit, cursor, value = '', state, action_type, sortOrder}) => {
|
||||
let query = UserModel.find();
|
||||
|
||||
if (action_type || state) {
|
||||
if (action_type || state || value.length > 0) {
|
||||
if (!user || !user.can(SEARCH_OTHER_USERS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value.length > 0) {
|
||||
|
||||
// Lowercase the search term and escape any regex characters.
|
||||
value = escapeRegExp(value).toLowerCase();
|
||||
|
||||
// Compile the prefix search regex.
|
||||
const $regex = new RegExp(`^${value}`);
|
||||
|
||||
// Merge in the regex params.
|
||||
query.merge({
|
||||
$or: [
|
||||
|
||||
// Search by a prefix match on the username.
|
||||
{
|
||||
lowercaseUsername: {
|
||||
$regex,
|
||||
},
|
||||
},
|
||||
|
||||
// Search by a prefix match on the email address.
|
||||
{
|
||||
profiles: {
|
||||
$elemMatch: {
|
||||
id: {
|
||||
$regex,
|
||||
},
|
||||
provider: 'local',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (state) {
|
||||
mergeState(query, state);
|
||||
}
|
||||
@@ -93,14 +128,6 @@ const getUsersByQuery = async ({user}, {ids, limit, cursor, state, action_type,
|
||||
}
|
||||
}
|
||||
|
||||
if (ids) {
|
||||
query = query.find({
|
||||
id: {
|
||||
$in: ids
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (cursor) {
|
||||
if (sortOrder === 'DESC') {
|
||||
query = query.where({
|
||||
@@ -125,6 +152,7 @@ const getUsersByQuery = async ({user}, {ids, limit, cursor, state, action_type,
|
||||
// Sort by created_at.
|
||||
query.sort({created_at: sortOrder === 'DESC' ? -1 : 1});
|
||||
|
||||
// Execute the query.
|
||||
const nodes = await query.exec();
|
||||
|
||||
// The hasNextPage is always handled the same (ask for one more than we need,
|
||||
|
||||
@@ -78,7 +78,7 @@ const RootQuery = {
|
||||
|
||||
// This endpoint is used for loading the user moderation queues (users whose username has been flagged),
|
||||
// so hide it in the event that we aren't an admin.
|
||||
async users(_, {query}, {user, loaders: {Users}}) {
|
||||
users(_, {query}, {user, loaders: {Users}}) {
|
||||
if (user == null || !user.can(SEARCH_OTHER_USERS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ const User = {
|
||||
|
||||
return tokens;
|
||||
},
|
||||
async ignoredUsers({id}, args, {user, loaders: {Users}}) {
|
||||
ignoredUsers({id}, args, {user, loaders: {Users}}) {
|
||||
|
||||
// Only allow a logged in user that is either the current user or is a staff
|
||||
// member to access the ignoredUsers of a given user.
|
||||
@@ -63,8 +63,7 @@ const User = {
|
||||
return [];
|
||||
}
|
||||
|
||||
const connection = await Users.getByQuery({ids: user.ignoresUsers});
|
||||
return connection.nodes;
|
||||
return Users.getByID.loadMany(user.ignoresUsers);
|
||||
},
|
||||
role({id, role}, _, {user}) {
|
||||
|
||||
|
||||
@@ -233,8 +233,12 @@ input UsersQuery {
|
||||
# Users returned will only be ones which have at least one action of this.
|
||||
action_type: ACTION_TYPE
|
||||
|
||||
# state will filter the users to a specific set of users that meet.
|
||||
state: UserStateInput
|
||||
|
||||
# value is the search string to use to search for a pa
|
||||
value: String = ""
|
||||
|
||||
# Limit the number of results to be returned.
|
||||
limit: Int = 10
|
||||
|
||||
|
||||
@@ -5,56 +5,6 @@ const errors = require('../../../errors');
|
||||
const authorization = require('../../../middleware/authorization');
|
||||
const Limit = require('../../../services/limit');
|
||||
|
||||
router.get('/', authorization.needed('ADMIN', 'MODERATOR'), async (req, res, next) => {
|
||||
|
||||
const {
|
||||
value = '',
|
||||
field = 'created_at',
|
||||
page = 1,
|
||||
asc = 'false',
|
||||
limit = 20 // Total Per Page
|
||||
} = req.query;
|
||||
|
||||
try {
|
||||
|
||||
const queryOpts = {
|
||||
sort: {[field]: (asc === 'true') ? 1 : -1},
|
||||
skip: (page - 1) * limit,
|
||||
limit
|
||||
};
|
||||
|
||||
let [result, count] = await Promise.all([
|
||||
UsersService
|
||||
.search(value)
|
||||
.sort(queryOpts.sort)
|
||||
.skip(parseInt(queryOpts.skip))
|
||||
.limit(parseInt(queryOpts.limit))
|
||||
.lean(),
|
||||
UsersService.search(value).count()
|
||||
]);
|
||||
|
||||
res.json({
|
||||
result,
|
||||
limit: Number(limit),
|
||||
count,
|
||||
page: Number(page),
|
||||
totalPages: Math.ceil(count / (limit === 0 ? 1 : limit))
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:user_id/role', authorization.needed('ADMIN', 'MODERATOR'), async (req, res, next) => {
|
||||
try {
|
||||
await UsersService.setRole(req.params.user_id, req.body.role);
|
||||
res.status(204).end();
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
||||
// create a local user.
|
||||
router.post('/', async (req, res, next) => {
|
||||
const {email, password, username} = req.body;
|
||||
|
||||
@@ -28,7 +28,6 @@ const MailerService = require('./mailer');
|
||||
const i18n = require('./i18n');
|
||||
const Wordlist = require('./wordlist');
|
||||
const DomainList = require('./domain_list');
|
||||
const {escapeRegExp} = require('./regex');
|
||||
|
||||
const EMAIL_CONFIRM_JWT_SUBJECT = 'email_confirm';
|
||||
const PASSWORD_RESET_JWT_SUBJECT = 'password_reset';
|
||||
@@ -759,47 +758,6 @@ class UsersService {
|
||||
return [user, loc];
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a user using a value which gets compared using a prefix match against
|
||||
* the user's email address and/or their username.
|
||||
* @param {String} value value to search by
|
||||
* @return {Promise}
|
||||
*/
|
||||
static search(value) {
|
||||
if (!value || typeof value !== 'string' || value.length === 0) {
|
||||
return UserModel.find({});
|
||||
}
|
||||
|
||||
value = escapeRegExp(value).toLowerCase();
|
||||
|
||||
// Compile the prefix search regex.
|
||||
const $regex = new RegExp(`^${value}`);
|
||||
|
||||
return UserModel.find({
|
||||
$or: [
|
||||
|
||||
// Search by a prefix match on the username.
|
||||
{
|
||||
lowercaseUsername: {
|
||||
$regex,
|
||||
},
|
||||
},
|
||||
|
||||
// Search by a prefix match on the email address.
|
||||
{
|
||||
profiles: {
|
||||
$elemMatch: {
|
||||
id: {
|
||||
$regex,
|
||||
},
|
||||
provider: 'local',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a count of the current users.
|
||||
* @return {Promise}
|
||||
|
||||
@@ -179,46 +179,6 @@ describe('services.UsersService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#search', () => {
|
||||
it('should return all the results without a value', async () => {
|
||||
expect(await UsersService.search()).to.have.length(3);
|
||||
});
|
||||
|
||||
it('should match the search terms', async () => {
|
||||
const tests = [
|
||||
{
|
||||
search: 'Stamp',
|
||||
results: 1,
|
||||
id: mockUsers[0].id,
|
||||
},
|
||||
{
|
||||
search: 'sockmonster',
|
||||
results: 1,
|
||||
id: mockUsers[1].id,
|
||||
},
|
||||
{
|
||||
search: 'marvel',
|
||||
results: 1,
|
||||
id: mockUsers[2].id,
|
||||
},
|
||||
{
|
||||
search: 'marvel',
|
||||
results: 1,
|
||||
id: mockUsers[2].id,
|
||||
},
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
const users = await UsersService.search(test.search);
|
||||
|
||||
expect(users).to.have.length(test.results);
|
||||
if (test.results === 1) {
|
||||
expect(users[0]).to.have.property('id', test.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
{func: 'changeUsername', okStatus: 'REJECTED', notOKStatus: 'UNSET', newStatus: 'CHANGED'},
|
||||
{func: 'setUsername', okStatus: 'UNSET', notOKStatus: 'REJECTED', newStatus: 'SET'},
|
||||
|
||||
Reference in New Issue
Block a user