user.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. const bcrypt = require('bcryptjs');
  2. const nanoid = require('nanoid');
  3. const subMinutes = require('date-fns/sub_minutes');
  4. const driver = require('./neo4j');
  5. exports.getUser = ({ email = '', apikey = '' }) =>
  6. new Promise((resolve, reject) => {
  7. const session = driver.session();
  8. session
  9. .readTransaction(tx =>
  10. tx.run(
  11. 'MATCH (u:USER) WHERE u.email = $email OR u.apikey = $apikey ' +
  12. 'OPTIONAL MATCH (u)-[r:RECEIVED]->(c) WITH u, collect(c.date) as cooldowns ' +
  13. 'OPTIONAL MATCH (u)-[:OWNS]->(d) RETURN u, d, cooldowns',
  14. {
  15. apikey,
  16. email,
  17. }
  18. )
  19. )
  20. .then(res => {
  21. session.close();
  22. const user = res.records.length && res.records[0].get('u').properties;
  23. const cooldowns = res.records.length && res.records[0].get('cooldowns');
  24. const domainProps = res.records.length && res.records[0].get('d');
  25. const domain = domainProps ? domainProps.properties.name : '';
  26. const homepage = domainProps ? domainProps.properties.homepage : '';
  27. const useHttps = domainProps ? domainProps.properties.useHttps : '';
  28. return resolve(user && { ...user, cooldowns, domain, homepage, useHttps });
  29. })
  30. .catch(err => reject(err));
  31. });
  32. exports.createUser = ({ email, password }) =>
  33. new Promise(async (resolve, reject) => {
  34. const session = driver.session();
  35. const salt = await bcrypt.genSalt(12);
  36. const hash = await bcrypt.hash(password, salt);
  37. const verificationToken = nanoid(40);
  38. session
  39. .writeTransaction(tx =>
  40. tx.run(
  41. 'MERGE (u:USER { email: $email }) ' +
  42. 'SET u.password = $hash , u.verified = $verified , ' +
  43. 'u.verificationToken = $verificationToken , u.createdAt = $createdAt ' +
  44. 'RETURN u',
  45. {
  46. email,
  47. hash,
  48. createdAt: new Date().toJSON(),
  49. verified: false,
  50. verificationToken,
  51. }
  52. )
  53. )
  54. .then(res => {
  55. session.close();
  56. const user = res.records[0].get('u').properties;
  57. return resolve(user);
  58. })
  59. .catch(err => reject(err));
  60. });
  61. exports.verifyUser = ({ verificationToken }) =>
  62. new Promise((resolve, reject) => {
  63. const session = driver.session();
  64. session
  65. .writeTransaction(tx =>
  66. tx.run(
  67. 'MATCH (u:USER { verificationToken: $verificationToken })' +
  68. 'SET u.verified = true SET u.verificationToken = NULL RETURN u',
  69. {
  70. verificationToken,
  71. }
  72. )
  73. )
  74. .then(({ records }) => {
  75. session.close();
  76. const user = records.length && records[0].get('u').properties;
  77. return resolve(user);
  78. })
  79. .catch(err => reject(err));
  80. });
  81. exports.changePassword = ({ email, password }) =>
  82. new Promise(async (resolve, reject) => {
  83. const session = driver.session();
  84. const salt = await bcrypt.genSalt(12);
  85. const hash = await bcrypt.hash(password, salt);
  86. session
  87. .writeTransaction(tx =>
  88. tx.run('MATCH (u:USER { email: $email }) SET u.password = $password RETURN u', {
  89. email,
  90. password: hash,
  91. })
  92. )
  93. .then(res => {
  94. session.close();
  95. const user = res.records.length && res.records[0].get('u').properties;
  96. return resolve(user);
  97. })
  98. .catch(err => session.close() || reject(err));
  99. });
  100. exports.generateApiKey = ({ email }) =>
  101. new Promise(async (resolve, reject) => {
  102. const session = driver.session();
  103. const apikey = nanoid(40);
  104. session
  105. .writeTransaction(tx =>
  106. tx.run('MATCH (u:USER { email: $email }) SET u.apikey = $apikey RETURN u', {
  107. email,
  108. apikey,
  109. })
  110. )
  111. .then(res => {
  112. session.close();
  113. const newApikey = res.records.length && res.records[0].get('u').properties.apikey;
  114. return resolve({ apikey: newApikey });
  115. })
  116. .catch(err => session.close() || reject(err));
  117. });
  118. exports.requestPasswordReset = ({ email }) =>
  119. new Promise(async (resolve, reject) => {
  120. const session = driver.session();
  121. const resetPasswordExprie = Date.now() + 3600000;
  122. const resetPasswordToken = nanoid(40);
  123. session
  124. .writeTransaction(tx =>
  125. tx.run(
  126. 'MATCH (u:USER { email: $email }) ' +
  127. 'SET u.resetPasswordToken = $resetPasswordToken ' +
  128. 'SET u.resetPasswordExprie = $resetPasswordExprie ' +
  129. 'RETURN u',
  130. {
  131. email,
  132. resetPasswordExprie,
  133. resetPasswordToken,
  134. }
  135. )
  136. )
  137. .then(res => {
  138. session.close();
  139. const user = res.records.length && res.records[0].get('u').properties;
  140. return resolve(user);
  141. })
  142. .catch(err => session.close() || reject(err));
  143. });
  144. exports.resetPassword = ({ resetPasswordToken }) =>
  145. new Promise((resolve, reject) => {
  146. const session = driver.session();
  147. session
  148. .writeTransaction(tx =>
  149. tx.run(
  150. 'MATCH (u:USER { resetPasswordToken: $resetPasswordToken })' +
  151. 'SET u.resetPasswordExprie = NULL SET u.resetPasswordToken = NULL RETURN u',
  152. {
  153. resetPasswordToken,
  154. }
  155. )
  156. )
  157. .then(({ records }) => {
  158. session.close();
  159. const user = records.length && records[0].get('u').properties;
  160. return resolve(user);
  161. })
  162. .catch(err => reject(err));
  163. });
  164. exports.addCooldown = ({ email }) =>
  165. new Promise((resolve, reject) => {
  166. const session = driver.session();
  167. session
  168. .writeTransaction(tx =>
  169. tx.run(
  170. 'MATCH (u:USER { email: $email }) ' +
  171. 'MERGE (u)-[r:RECEIVED]->(c:COOLDOWN { date: $date }) ' +
  172. 'RETURN COUNT(r) as count',
  173. {
  174. date: new Date().toJSON(),
  175. email,
  176. }
  177. )
  178. )
  179. .then(({ records }) => {
  180. session.close();
  181. const count = records.length && records[0].get('count').toNumber();
  182. return resolve({ count });
  183. })
  184. .catch(err => reject(err));
  185. });
  186. exports.getCooldowns = ({ email }) =>
  187. new Promise((resolve, reject) => {
  188. const session = driver.session();
  189. session
  190. .writeTransaction(tx =>
  191. tx.run(
  192. 'MATCH (u:USER { email: $email }) MATCH (u)-[r:RECEIVED]->(c) RETURN c.date as date',
  193. {
  194. date: new Date().toJSON(),
  195. email,
  196. }
  197. )
  198. )
  199. .then(({ records = [] }) => {
  200. session.close();
  201. const cooldowns = records.map(record => record.get('date'));
  202. return resolve({ cooldowns });
  203. })
  204. .catch(err => reject(err));
  205. });
  206. exports.banUser = ({ email }) =>
  207. new Promise((resolve, reject) => {
  208. const session = driver.session();
  209. session
  210. .writeTransaction(tx =>
  211. tx.run('MATCH (u:USER { email: $email }) SET u.banned = true RETURN u', {
  212. email,
  213. })
  214. )
  215. .then(({ records = [] }) => {
  216. session.close();
  217. const user = records.length && records[0].get('u');
  218. return resolve({ user });
  219. })
  220. .catch(err => reject(err));
  221. });
  222. exports.addIPCooldown = async ip => {
  223. const session = driver.session();
  224. const { records = [] } = await session.writeTransaction(tx =>
  225. tx.run(
  226. 'MERGE (i:IP { ip: $ip }) ' +
  227. 'MERGE (i)-[r:RECEIVED]->(c:COOLDOWN { date: $date }) ' +
  228. 'RETURN COUNT(r) as count',
  229. {
  230. date: new Date().toJSON(),
  231. ip,
  232. }
  233. )
  234. );
  235. session.close();
  236. const count = records.length && records[0].get('count').toNumber();
  237. return count;
  238. };
  239. exports.getIPCooldown = async ip => {
  240. const session = driver.session();
  241. const { records = [] } = await session.readTransaction(tx =>
  242. tx.run(
  243. 'MATCH (i:IP { ip: $ip }) ' +
  244. 'MATCH (i)-[:RECEIVED]->(c:COOLDOWN) ' +
  245. 'WHERE c.date > $date ' +
  246. 'RETURN c.date as date',
  247. {
  248. date: subMinutes(new Date(), Number(process.env.NON_USER_COOLDOWN)).toJSON(),
  249. ip,
  250. }
  251. )
  252. );
  253. session.close();
  254. const count = records.length && records[0].get('date');
  255. return count;
  256. };
  257. exports.clearIPs = async () => {
  258. const session = driver.session();
  259. await session.writeTransaction(tx =>
  260. tx.run('MATCH (i:IP)-[:RECEIVED]->(c:COOLDOWN) WHERE c.date < $date DETACH DELETE i, c', {
  261. date: subMinutes(new Date(), Number(process.env.NON_USER_COOLDOWN)).toJSON(),
  262. })
  263. );
  264. session.close();
  265. };