Skip to content

Commit

Permalink
Merge pull request #364 from steemit/search-fingerprint
Browse files Browse the repository at this point in the history
 Expose fingerprint to admin search.
  • Loading branch information
gl2748 authored Jun 19, 2018
2 parents fa3cf0b + a2996ea commit 3a6c03c
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 100 deletions.
2 changes: 1 addition & 1 deletion admin/src/components/signup-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ enum TableAction {

const ITEMS_PER_PAGE = 50

const SEARCH_FILTER_PATTERN = /(!?\w+):([^ ]+)/g
const SEARCH_FILTER_PATTERN = /(!?[a-z.]*):([^ ]+)/g

function parseSearchFilter(filter: string) {
const filters: SignupListFilter[] = []
Expand Down
6 changes: 3 additions & 3 deletions db/seeders/users-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ module.exports = {
phone_code_attempts: 0,
phone_code: null,
ip: '75.662.77.122',
fingerprint: '{"date": "Fri Sep 15 2017 10:38:36 GMT+0200 (Paris, Madrid (heure d’été)", "device": {"renderer": "ANGLE (Intel(R) HD Graphics 4000 Direct3D11 vs_5_0 ps_5_0)", "vendor": "Google Inc."}, "lang": "fr-FR,fr,en-US,en,ms", "ref": "https://jsfiddle.net/", "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"}',
fingerprint: '{"date": "Fri Sep 15 2017 10:38:36 GMT+0200 (Paris, Madrid (heure d’été)", "device": {"renderer": "ANGLE (Intel(R) HD Graphics 4000 Direct3D11 vs_5_0 ps_5_0)", "vendor": "Google Inc."}, "lang": "fr-FR,fr,en-US,en,ms", "ref": "https://vvork.com/", "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"}',
creation_hash: '5a234b0a964be4a73c0bf78df675038e1e297c4726cd7340bfeeaf036ceeb885',
metadata: JSON.stringify({ query: { uid: '444' } }),
account_is_created: false,
Expand All @@ -205,7 +205,7 @@ module.exports = {
phone_code_attempts: 0,
phone_code: '5555',
ip: '75.662.77.122',
fingerprint: '{"date": "Fri Sep 15 2017 10:38:36 GMT+0200 (Paris, Madrid (heure d’été)", "device": {"renderer": "ANGLE (Intel(R) HD Graphics 4000 Direct3D11 vs_5_0 ps_5_0)", "vendor": "Google Inc."}, "lang": "fr-FR,fr,en-US,en,ms", "ref": "https://jsfiddle.net/", "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"}',
fingerprint: '{"date": "Fri Sep 15 2017 10:38:36 GMT+0200 (Paris, Madrid (heure d’été)", "device": {"renderer": "ANGLE (Intel(R) HD Graphics 4000 Direct3D11 vs_5_0 ps_5_0)", "vendor": "Google Inc."}, "lang": "fr-FR,fr,en-US,en,ms", "ref": "https://google.com/", "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"}',
creation_hash: '5a234b0a964be4a73c0bf78df675038e1e297c4726cd7340bfeeaf036ceeb885',
metadata: JSON.stringify({ query: { uid: '555' } }),
account_is_created: false,
Expand All @@ -226,7 +226,7 @@ module.exports = {
phone_code_attempts: 1,
phone_code: '66666',
ip: '75.662.77.122',
fingerprint: '{"date": "Fri Sep 15 2017 10:38:36 GMT+0200 (Paris, Madrid (heure d’été)", "device": {"renderer": "ANGLE (Intel(R) HD Graphics 4000 Direct3D11 vs_5_0 ps_5_0)", "vendor": "Google Inc."}, "lang": "fr-FR,fr,en-US,en,ms", "ref": "https://jsfiddle.net/", "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"}',
fingerprint: '{"date": "Fri Sep 15 2017 10:38:36 GMT+0200 (Paris, Madrid (heure d’été)", "device": {"renderer": "hello world INC 2018", "vendor": "Cool` Inc."}, "lang": "fr-FR,fr,en-US,en,ms", "ref": "https://steemit.com/", "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"}',
creation_hash: '5a234b0a964be4a73c0bf78df675038e1e297c4726cd7340bfeeaf036ceeb885',
metadata: JSON.stringify({ query: { uid: '666' } }),
account_is_created: false,
Expand Down
4 changes: 4 additions & 0 deletions helpers/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ async function emailIsInUse(email) {
const logAction = async data => db.actions.create(data);

const findUser = async where => db.users.findOne(where);
const findUsers = async query => db.users.findAll(query);
const countUsers = async where => db.users.count({ where });

const usernameIsBooked = async username => {
const user = await findUser({
Expand Down Expand Up @@ -83,6 +85,8 @@ module.exports = {
emailIsInUse,
logAction,
findUser,
findUsers,
countUsers,
usernameIsBooked,
createUser,
phoneIsInUse,
Expand Down
98 changes: 2 additions & 96 deletions routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const db = require('./../db/models');
const geoip = require('../helpers/maxmind');
const services = require('../helpers/services');
const { OAuth2Client } = require('google-auth-library');
const adminHandlers = require('./adminHandlers');

const { Sequelize } = db;

Expand Down Expand Up @@ -168,102 +169,7 @@ addHandler('/get_signup', async req => {
return { user, actions, location };
});

addHandler('/list_signups', async req => {
const { limit, order, offset, filters } = req.body;
const query = {
order: order || [['created_at', 'DESC']],
limit: limit ? Math.min(limit, 100) : 10,
};
if (offset) {
query.offset = offset;
}
if (Array.isArray(filters) && filters.length > 0) {
const {
or,
eq,
ne,
like,
notLike,
and,
gte,
lte,
regexp,
notRegexp,
} = Sequelize.Op;
const andList = [];
for (const filter of filters) {
// eslint-disable-line
const { value } = filter;
let name = filter.name;
let negate = false;
if (name[0] === '!') {
negate = true;
name = name.slice(1);
}
const nLike = negate ? notLike : like;
const nEq = negate ? ne : eq;
const nRegexp = negate ? notRegexp : regexp;
switch (name) {
case 'text':
andList.push({
[negate ? and : or]: [
{ email: { [nLike]: `%${value}%` } },
{ username: { [nLike]: `%${value}%` } },
{ phone_number: { [nLike]: `%${value}%` } },
{ fingerprint: { [nLike]: `%${value}%` } },
],
});
break;
case 'status':
andList.push({ status: { [nEq]: value } });
break;
case 'ip':
andList.push({ ip: { [nEq]: value } });
break;
case 'username':
andList.push({ username: { [nEq]: value } });
break;
case 'phone':
case 'phone_number':
andList.push({ phone_number: { [nEq]: value } });
break;
case 'email':
andList.push({ email: { [nEq]: value } });
break;
case 'username_re':
andList.push({ username: { [nRegexp]: value } });
break;
case 'email_re':
andList.push({ email: { [nRegexp]: value } });
break;
case 'phone_re':
case 'phone_number_re':
andList.push({ phone_number: { [nRegexp]: value } });
break;
case 'note':
andList.push({ review_note: { [nLike]: `%${value}%` } });
break;
case 'note_re':
andList.push({ review_note: { [nRegexp]: value } });
break;
case 'from':
andList.push({ created_at: { [gte]: new Date(value) } });
break;
case 'to':
andList.push({ created_at: { [lte]: new Date(value) } });
break;
default:
throw new Error(`Unknown filter: ${name}`);
}
}
query.where = { [and]: andList };
}
const [total, users] = await Promise.all([
db.users.count({ where: query.where }),
db.users.findAll(query),
]);
return { total, users, query };
});
addHandler('/list_signups', adminHandlers.listSignups);

addHandler('/approve_signups', async req => {
const { ids } = req.body;
Expand Down
134 changes: 134 additions & 0 deletions routes/adminHandlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
const database = require('../helpers/database');

const { Sequelize } = require('../db/models');

async function listSignups(req) {
const { limit, order, offset, filters } = req.body;
const query = {
order: order || [['created_at', 'DESC']],
limit: limit ? Math.min(limit, 100) : 10,
};
if (offset) {
query.offset = offset;
}
if (Array.isArray(filters) && filters.length > 0) {
const {
where,
literal,
Op: { or, eq, ne, like, notLike, and, gte, lte, regexp, notRegexp },
} = Sequelize;

const andList = [];
for (const filter of filters) {
// eslint-disable-line
const { value } = filter;
let name = filter.name;
let negate = false;
if (name[0] === '!') {
negate = true;
name = name.slice(1);
}
const nLike = negate ? notLike : like;
const nEq = negate ? ne : eq;
const nRegexp = negate ? notRegexp : regexp;
switch (name) {
case 'text':
andList.push({
[negate ? and : or]: [
{ email: { [nLike]: `%${value}%` } },
{ username: { [nLike]: `%${value}%` } },
{ phone_number: { [nLike]: `%${value}%` } },
],
});
break;
case 'status':
andList.push({ status: { [nEq]: value } });
break;
case 'ip':
andList.push({ ip: { [nEq]: value } });
break;
case 'username':
andList.push({ username: { [nEq]: value } });
break;
case 'phone':
case 'phone_number':
andList.push({ phone_number: { [nEq]: value } });
break;
case 'email':
andList.push({ email: { [nEq]: value } });
break;
case 'username_re':
andList.push({ username: { [nRegexp]: value } });
break;
case 'email_re':
andList.push({ email: { [nRegexp]: value } });
break;
case 'phone_re':
case 'phone_number_re':
andList.push({ phone_number: { [nRegexp]: value } });
break;
case 'note':
andList.push({ review_note: { [nLike]: `%${value}%` } });
break;
case 'note_re':
andList.push({ review_note: { [nRegexp]: value } });
break;
case 'from':
andList.push({ created_at: { [gte]: new Date(value) } });
break;
case 'to':
andList.push({ created_at: { [lte]: new Date(value) } });
break;
case 'fingerprint.ua':
andList.push({
fingerprint: where(literal("fingerprint -> '$.ua'"), {
[nRegexp]: value,
}),
});
break;
case 'fingerprint.ref':
andList.push({
fingerprint: where(literal("fingerprint -> '$.ref'"), {
[nRegexp]: value,
}),
});
break;
case 'fingerprint.lang':
andList.push({
fingerprint: where(literal("fingerprint -> '$.lang'"), {
[nRegexp]: value,
}),
});
break;
case 'fingerprint.device.vendor':
andList.push({
fingerprint: where(
literal("fingerprint -> '$.device.vendor'"),
{ [nRegexp]: value }
),
});
break;
case 'fingerprint.device.renderer':
andList.push({
fingerprint: where(
literal("fingerprint -> '$.device.renderer'"),
{ [nRegexp]: value }
),
});
break;
default:
throw new Error(`Unknown filter: ${name}`);
}
}
query.where = { [and]: andList };
}
const [total, users] = await Promise.all([
database.countUsers(query.where),
database.findUsers(query),
]);
return { total, users, query };
}

module.exports = {
listSignups,
};
Loading

0 comments on commit 3a6c03c

Please sign in to comment.