|
|
@@ -1,229 +1,154 @@
|
|
|
const bcrypt = require('bcryptjs');
|
|
|
const nanoid = require('nanoid');
|
|
|
+const uuid = require('uuid/v4');
|
|
|
const subMinutes = require('date-fns/sub_minutes');
|
|
|
-const driver = require('./neo4j');
|
|
|
-
|
|
|
-exports.getUser = async ({ email = '', apikey = '' }) => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.readTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MATCH (u:USER) WHERE u.email = $email OR u.apikey = $apikey ' +
|
|
|
- 'OPTIONAL MATCH (u)-[r:RECEIVED]->(c) WITH u, collect(c.date) as cooldowns ' +
|
|
|
- 'OPTIONAL MATCH (u)-[:OWNS]->(d) RETURN u, d, cooldowns',
|
|
|
- {
|
|
|
- apikey,
|
|
|
- email,
|
|
|
- }
|
|
|
- )
|
|
|
- );
|
|
|
- session.close();
|
|
|
- const user = records.length && records[0].get('u').properties;
|
|
|
- const cooldowns = records.length && records[0].get('cooldowns');
|
|
|
- const domainProps = records.length && records[0].get('d');
|
|
|
- const domain = domainProps ? domainProps.properties.name : '';
|
|
|
- const homepage = domainProps ? domainProps.properties.homepage : '';
|
|
|
- const useHttps = domainProps ? domainProps.properties.useHttps : '';
|
|
|
- return user && { ...user, cooldowns, domain, homepage, useHttps };
|
|
|
+const addMinutes = require('date-fns/add_minutes');
|
|
|
+const User = require('../models/user');
|
|
|
+const Ip = require('../models/ip');
|
|
|
+
|
|
|
+exports.getUser = async (emailOrKey = '') => {
|
|
|
+ const user = await User.findOne({
|
|
|
+ $or: [{ email: emailOrKey }, { apikey: emailOrKey }],
|
|
|
+ }).lean();
|
|
|
+ // TODO: Get domains
|
|
|
+
|
|
|
+ // const session = driver.session();
|
|
|
+ // const { records = [] } = await session.readTransaction(tx =>
|
|
|
+ // tx.run(
|
|
|
+ // 'MATCH (u:USER) WHERE u.email = $email OR u.apikey = $apikey ' +
|
|
|
+ // 'OPTIONAL MATCH (u)-[r:RECEIVED]->(c) WITH u, collect(c.date) as cooldowns ' +
|
|
|
+ // 'OPTIONAL MATCH (u)-[:OWNS]->(d) RETURN u, d, cooldowns',
|
|
|
+ // {
|
|
|
+ // apikey,
|
|
|
+ // email,
|
|
|
+ // }
|
|
|
+ // )
|
|
|
+ // );
|
|
|
+ // session.close();
|
|
|
+ // const user = records.length && records[0].get('u').properties;
|
|
|
+ // const cooldowns = records.length && records[0].get('cooldowns');
|
|
|
+ // const domainProps = records.length && records[0].get('d');
|
|
|
+ // const domain = domainProps ? domainProps.properties.name : '';
|
|
|
+ // const homepage = domainProps ? domainProps.properties.homepage : '';
|
|
|
+ // const useHttps = domainProps ? domainProps.properties.useHttps : '';
|
|
|
+ // return user && { ...user, cooldowns, domain, homepage, useHttps };
|
|
|
+ return user;
|
|
|
};
|
|
|
|
|
|
-exports.createUser = async ({ email, password }) => {
|
|
|
- const session = driver.session();
|
|
|
+exports.createUser = async (email, password) => {
|
|
|
const salt = await bcrypt.genSalt(12);
|
|
|
- const hash = await bcrypt.hash(password, salt);
|
|
|
- const verificationToken = nanoid(40);
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MERGE (u:USER { email: $email }) ' +
|
|
|
- 'SET u.password = $hash , u.verified = $verified , ' +
|
|
|
- 'u.verificationToken = $verificationToken , u.verificationExpires = $verificationExpires, u.createdAt = $createdAt ' +
|
|
|
- 'RETURN u',
|
|
|
- {
|
|
|
- email,
|
|
|
- hash,
|
|
|
- createdAt: new Date().toJSON(),
|
|
|
- verified: false,
|
|
|
- verificationToken,
|
|
|
- verificationExpires: Date.now() + 3600000,
|
|
|
- }
|
|
|
- )
|
|
|
+ const hashedPassword = await bcrypt.hash(password, salt);
|
|
|
+
|
|
|
+ const user = await User.findOneAndUpdate(
|
|
|
+ { email },
|
|
|
+ {
|
|
|
+ email,
|
|
|
+ password: hashedPassword,
|
|
|
+ verificationToken: uuid(),
|
|
|
+ verificationExpires: addMinutes(new Date(), 60),
|
|
|
+ },
|
|
|
+ { new: true, upsert: true, runValidators: true, setDefaultsOnInsert: true }
|
|
|
);
|
|
|
- session.close();
|
|
|
- const user = records[0].get('u').properties;
|
|
|
+
|
|
|
return user;
|
|
|
};
|
|
|
|
|
|
-exports.verifyUser = async ({ verificationToken }) => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MATCH (u:USER) ' +
|
|
|
- 'WHERE u.verificationToken = $verificationToken AND u.verificationExpires > $currentTime ' +
|
|
|
- 'SET u.verified = true, u.verificationToken = NULL, u.verificationExpires = NULL RETURN u',
|
|
|
- {
|
|
|
- verificationToken,
|
|
|
- currentTime: Date.now(),
|
|
|
- }
|
|
|
- )
|
|
|
+exports.verifyUser = async verificationToken => {
|
|
|
+ const user = await User.findOneAndUpdate(
|
|
|
+ { verificationToken, verificationExpires: { $gt: new Date() } },
|
|
|
+ {
|
|
|
+ verified: true,
|
|
|
+ verificationToken: undefined,
|
|
|
+ verificationExpires: undefined,
|
|
|
+ },
|
|
|
+ { new: true }
|
|
|
);
|
|
|
- session.close();
|
|
|
- const user = records.length && records[0].get('u').properties;
|
|
|
+
|
|
|
return user;
|
|
|
};
|
|
|
|
|
|
-exports.changePassword = async ({ email, password }) => {
|
|
|
- const session = driver.session();
|
|
|
+exports.changePassword = async (id, newPassword) => {
|
|
|
const salt = await bcrypt.genSalt(12);
|
|
|
- const hash = await bcrypt.hash(password, salt);
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run('MATCH (u:USER { email: $email }) SET u.password = $password RETURN u', {
|
|
|
- email,
|
|
|
- password: hash,
|
|
|
- })
|
|
|
- );
|
|
|
- session.close();
|
|
|
- const user = records.length && records[0].get('u').properties;
|
|
|
+ const password = await bcrypt.hash(newPassword, salt);
|
|
|
+
|
|
|
+ const user = await User.findByIdAndUpdate(id, { password }, { new: true });
|
|
|
+
|
|
|
return user;
|
|
|
};
|
|
|
|
|
|
-exports.generateApiKey = async ({ email }) => {
|
|
|
- const session = driver.session();
|
|
|
+exports.generateApiKey = async id => {
|
|
|
const apikey = nanoid(40);
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run('MATCH (u:USER { email: $email }) SET u.apikey = $apikey RETURN u', {
|
|
|
- email,
|
|
|
- apikey,
|
|
|
- })
|
|
|
- );
|
|
|
- session.close();
|
|
|
- const newApikey = records.length && records[0].get('u').properties.apikey;
|
|
|
- return { apikey: newApikey };
|
|
|
-};
|
|
|
|
|
|
-exports.requestPasswordReset = async ({ email }) => {
|
|
|
- const session = driver.session();
|
|
|
- const resetPasswordToken = nanoid(40);
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MATCH (u:USER { email: $email }) ' +
|
|
|
- 'SET u.resetPasswordToken = $resetPasswordToken ' +
|
|
|
- 'SET u.resetPasswordExpires = $resetPasswordExpires ' +
|
|
|
- 'RETURN u',
|
|
|
- {
|
|
|
- email,
|
|
|
- resetPasswordExpires: Date.now() + 3600000,
|
|
|
- resetPasswordToken,
|
|
|
- }
|
|
|
- )
|
|
|
- );
|
|
|
- session.close();
|
|
|
- const user = records.length && records[0].get('u').properties;
|
|
|
+ const user = await User.findByIdAndUpdate(id, { apikey }, { new: true });
|
|
|
+
|
|
|
return user;
|
|
|
};
|
|
|
|
|
|
-exports.resetPassword = async ({ resetPasswordToken }) => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MATCH (u:USER) ' +
|
|
|
- 'WHERE u.resetPasswordToken = $resetPasswordToken AND u.resetPasswordExpires > $currentTime ' +
|
|
|
- 'SET u.resetPasswordExpires = NULL, u.resetPasswordToken = NULL RETURN u',
|
|
|
- {
|
|
|
- resetPasswordToken,
|
|
|
- currentTime: Date.now(),
|
|
|
- }
|
|
|
- )
|
|
|
+exports.requestPasswordReset = async email => {
|
|
|
+ const resetPasswordToken = uuid();
|
|
|
+
|
|
|
+ const user = await User.findOneAndUpdate(
|
|
|
+ { email },
|
|
|
+ {
|
|
|
+ resetPasswordToken,
|
|
|
+ resetPasswordExpires: addMinutes(new Date(), 30),
|
|
|
+ },
|
|
|
+ { new: true }
|
|
|
);
|
|
|
- session.close();
|
|
|
- const user = records.length && records[0].get('u').properties;
|
|
|
+
|
|
|
return user;
|
|
|
};
|
|
|
|
|
|
-exports.addCooldown = async ({ email }) => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MATCH (u:USER { email: $email }) ' +
|
|
|
- 'MERGE (u)-[r:RECEIVED]->(c:COOLDOWN { date: $date }) ' +
|
|
|
- 'RETURN COUNT(r) as count',
|
|
|
- {
|
|
|
- date: new Date().toJSON(),
|
|
|
- email,
|
|
|
- }
|
|
|
- )
|
|
|
+exports.resetPassword = async resetPasswordToken => {
|
|
|
+ const user = await User.findOneAndUpdate(
|
|
|
+ { resetPasswordToken, resetPasswordExpires: { $gt: new Date() } },
|
|
|
+ { resetPasswordExpires: undefined, resetPasswordToken: undefined },
|
|
|
+ { new: true }
|
|
|
);
|
|
|
- session.close();
|
|
|
- const count = records.length && records[0].get('count').toNumber();
|
|
|
- return { count };
|
|
|
-};
|
|
|
|
|
|
-exports.getCooldowns = async ({ email }) => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run('MATCH (u:USER { email: $email }) MATCH (u)-[r:RECEIVED]->(c) RETURN c.date as date', {
|
|
|
- date: new Date().toJSON(),
|
|
|
- email,
|
|
|
- })
|
|
|
- );
|
|
|
- session.close();
|
|
|
- const cooldowns = records.map(record => record.get('date'));
|
|
|
- return { cooldowns };
|
|
|
+ return user;
|
|
|
};
|
|
|
|
|
|
-exports.banUser = async ({ email }) => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run('MATCH (u:USER { email: $email }) SET u.banned = true RETURN u', {
|
|
|
- email,
|
|
|
- })
|
|
|
+exports.addCooldown = async id => {
|
|
|
+ const user = await User.findByIdAndUpdate(
|
|
|
+ id,
|
|
|
+ { $push: { cooldowns: new Date() } },
|
|
|
+ { new: true }
|
|
|
);
|
|
|
- session.close();
|
|
|
- const user = records.length && records[0].get('u');
|
|
|
- return { user };
|
|
|
+
|
|
|
+ return user;
|
|
|
};
|
|
|
|
|
|
-exports.addIPCooldown = async ip => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.writeTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MERGE (i:IP { ip: $ip }) ' +
|
|
|
- 'MERGE (i)-[r:RECEIVED]->(c:COOLDOWN { date: $date }) ' +
|
|
|
- 'RETURN COUNT(r) as count',
|
|
|
- {
|
|
|
- date: new Date().toJSON(),
|
|
|
- ip,
|
|
|
- }
|
|
|
- )
|
|
|
+exports.banUser = async id => {
|
|
|
+ const user = await User.findByIdAndUpdate(
|
|
|
+ id,
|
|
|
+ {
|
|
|
+ banned: true,
|
|
|
+ },
|
|
|
+ { new: true }
|
|
|
);
|
|
|
- session.close();
|
|
|
- const count = records.length && records[0].get('count').toNumber();
|
|
|
- return count;
|
|
|
+
|
|
|
+ return user;
|
|
|
};
|
|
|
|
|
|
-exports.getIPCooldown = async ip => {
|
|
|
- const session = driver.session();
|
|
|
- const { records = [] } = await session.readTransaction(tx =>
|
|
|
- tx.run(
|
|
|
- 'MATCH (i:IP { ip: $ip }) ' +
|
|
|
- 'MATCH (i)-[:RECEIVED]->(c:COOLDOWN) ' +
|
|
|
- 'WHERE c.date > $date ' +
|
|
|
- 'RETURN c.date as date',
|
|
|
- {
|
|
|
- date: subMinutes(new Date(), Number(process.env.NON_USER_COOLDOWN)).toJSON(),
|
|
|
- ip,
|
|
|
- }
|
|
|
- )
|
|
|
+exports.addIp = async newIp => {
|
|
|
+ const ip = await Ip.findOneAndUpdate(
|
|
|
+ { ip: newIp },
|
|
|
+ { ip: newIp, createdAt: new Date() },
|
|
|
+ { new: true, upsert: true, runValidators: true }
|
|
|
);
|
|
|
- session.close();
|
|
|
- const count = records.length && records[0].get('date');
|
|
|
- return count;
|
|
|
+ return ip;
|
|
|
};
|
|
|
|
|
|
-exports.clearIPs = async () => {
|
|
|
- const session = driver.session();
|
|
|
- await session.writeTransaction(tx =>
|
|
|
- tx.run('MATCH (i:IP)-[:RECEIVED]->(c:COOLDOWN) WHERE c.date < $date DETACH DELETE i, c', {
|
|
|
- date: subMinutes(new Date(), Number(process.env.NON_USER_COOLDOWN)).toJSON(),
|
|
|
- })
|
|
|
- );
|
|
|
- session.close();
|
|
|
+exports.getIp = async ip => {
|
|
|
+ const matchedIp = await Ip.findOne({
|
|
|
+ ip,
|
|
|
+ createdAt: { $gt: subMinutes(new Date(), Number(process.env.NON_USER_COOLDOWN)) },
|
|
|
+ });
|
|
|
+ return matchedIp;
|
|
|
};
|
|
|
+
|
|
|
+exports.clearIps = async () =>
|
|
|
+ Ip.deleteMany({
|
|
|
+ createdAt: { $lt: subMinutes(new Date(), Number(process.env.NON_USER_COOLDOWN)) },
|
|
|
+ });
|