users.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import config from "config";
  2. import CoreClass from "../core";
  3. let UsersModule;
  4. let MailModule;
  5. let CacheModule;
  6. let DBModule;
  7. let PlaylistsModule;
  8. let WSModule;
  9. let MediaModule;
  10. class _UsersModule extends CoreClass {
  11. // eslint-disable-next-line require-jsdoc
  12. constructor() {
  13. super("users");
  14. UsersModule = this;
  15. }
  16. /**
  17. * Initialises the app module
  18. * @returns {Promise} - returns promise (reject, resolve)
  19. */
  20. async initialize() {
  21. DBModule = this.moduleManager.modules.db;
  22. MailModule = this.moduleManager.modules.mail;
  23. WSModule = this.moduleManager.modules.ws;
  24. CacheModule = this.moduleManager.modules.cache;
  25. MediaModule = this.moduleManager.modules.media;
  26. this.userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" });
  27. this.dataRequestModel = await DBModule.runJob("GET_MODEL", { modelName: "dataRequest" });
  28. this.stationModel = await DBModule.runJob("GET_MODEL", { modelName: "station" });
  29. this.playlistModel = await DBModule.runJob("GET_MODEL", { modelName: "playlist" });
  30. this.activityModel = await DBModule.runJob("GET_MODEL", { modelName: "activity" });
  31. this.dataRequestEmail = await MailModule.runJob("GET_SCHEMA_ASYNC", { schemaName: "dataRequest" });
  32. }
  33. /**
  34. * Removes a user and associated data
  35. * @param {object} payload - object that contains the payload
  36. * @param {string} payload.userId - id of the user to remove
  37. * @returns {Promise} - returns a promise (resolve, reject)
  38. */
  39. async REMOVE_USER(payload) {
  40. const { userId } = payload;
  41. // Create data request, in case the process fails halfway through. An admin can finish the removal manually
  42. const dataRequest = await UsersModule.dataRequestModel.create({ userId, type: "remove" });
  43. await WSModule.runJob(
  44. "EMIT_TO_ROOM",
  45. {
  46. room: "admin.users",
  47. args: ["event:admin.dataRequests.created", { data: { request: dataRequest } }]
  48. },
  49. this
  50. );
  51. if (config.get("sendDataRequestEmails")) {
  52. const adminUsers = await UsersModule.userModel.find({ role: "admin" });
  53. const to = adminUsers.map(adminUser => adminUser.email.address);
  54. await UsersModule.dataRequestEmail(to, userId, "remove");
  55. }
  56. // Delete activities
  57. await UsersModule.activityModel.deleteMany({ userId });
  58. // Delete stations and associated data
  59. const stations = await UsersModule.stationModel.find({ owner: userId });
  60. const stationJobs = stations.map(station => async () => {
  61. const { _id: stationId } = station;
  62. await UsersModule.stationModel.deleteOne({ _id: stationId });
  63. await CacheModule.runJob("HDEL", { table: "stations", key: stationId }, this);
  64. if (!station.playlist) return;
  65. await PlaylistsModule.runJob("DELETE_PLAYLIST", { playlistId: station.playlist }, this);
  66. });
  67. await Promise.all(stationJobs);
  68. // Remove user as dj
  69. await UsersModule.stationModel.updateMany({ djs: userId }, { $pull: { djs: userId } });
  70. // Collect songs to adjust ratings for later
  71. const likedPlaylist = await UsersModule.playlistModel.findOne({ createdBy: userId, type: "user-liked" });
  72. const dislikedPlaylist = await UsersModule.playlistModel.findOne({ createdBy: userId, type: "user-disliked" });
  73. const songsToAdjustRatings = [
  74. ...likedPlaylist.songs.map(({ mediaSource }) => mediaSource),
  75. ...dislikedPlaylist.songs.map(({ mediaSource }) => mediaSource)
  76. ];
  77. // Delete playlists created by user
  78. await UsersModule.playlistModel.deleteMany({ createdBy: userId });
  79. // TODO Maybe we don't need to wait for this to finish?
  80. // Recalculate ratings of songs the user liked/disliked
  81. const recalculateRatingsJobs = songsToAdjustRatings.map(songsToAdjustRating =>
  82. MediaModule.runJob("RECALCULATE_RATINGS", { mediaSource: songsToAdjustRating }, this)
  83. );
  84. await Promise.all(recalculateRatingsJobs);
  85. // Delete user object
  86. await UsersModule.userModel.deleteMany({ _id: userId });
  87. // Remove sessions from Redis and MongoDB
  88. await CacheModule.runJob("PUB", { channel: "user.removeSessions", value: userId }, this);
  89. const sessions = await CacheModule.runJob("HGETALL", { table: "sessions" }, this);
  90. const sessionIds = Object.keys(sessions);
  91. const sessionJobs = sessionIds.map(sessionId => async () => {
  92. const session = sessions[sessionId];
  93. if (!session || session.userId !== userId) return;
  94. await CacheModule.runJob("HDEL", { table: "sessions", key: sessionId }, this);
  95. });
  96. await Promise.all(sessionJobs);
  97. await CacheModule.runJob(
  98. "PUB",
  99. {
  100. channel: "user.removeAccount",
  101. value: userId
  102. },
  103. this
  104. );
  105. }
  106. // EXAMPLE_JOB() {
  107. // return new Promise((resolve, reject) => {
  108. // if (true) resolve({});
  109. // else reject(new Error("Nothing changed."));
  110. // });
  111. // }
  112. }
  113. export default new _UsersModule();