user.js 7.0 KB

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