reports.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import async from "async";
  2. import { isAdminRequired, isLoginRequired } from "./hooks";
  3. import moduleManager from "../../index";
  4. const DBModule = moduleManager.modules.db;
  5. const UtilsModule = moduleManager.modules.utils;
  6. const IOModule = moduleManager.modules.io;
  7. const SongsModule = moduleManager.modules.songs;
  8. const CacheModule = moduleManager.modules.cache;
  9. const reportableIssues = [
  10. {
  11. name: "Video",
  12. reasons: ["Doesn't exist", "It's private", "It's not available in my country"]
  13. },
  14. {
  15. name: "Title",
  16. reasons: ["Incorrect", "Inappropriate"]
  17. },
  18. {
  19. name: "Duration",
  20. reasons: ["Skips too soon", "Skips too late", "Starts too soon", "Skips too late"]
  21. },
  22. {
  23. name: "Artists",
  24. reasons: ["Incorrect", "Inappropriate"]
  25. },
  26. {
  27. name: "Thumbnail",
  28. reasons: ["Incorrect", "Inappropriate", "Doesn't exist"]
  29. }
  30. ];
  31. CacheModule.runJob("SUB", {
  32. channel: "report.resolve",
  33. cb: reportId => {
  34. IOModule.runJob("EMIT_TO_ROOM", {
  35. room: "admin.reports",
  36. args: ["event:admin.report.resolved", reportId]
  37. });
  38. }
  39. });
  40. CacheModule.runJob("SUB", {
  41. channel: "report.create",
  42. cb: report => {
  43. IOModule.runJob("EMIT_TO_ROOM", {
  44. room: "admin.reports",
  45. args: ["event:admin.report.created", report]
  46. });
  47. }
  48. });
  49. export default {
  50. /**
  51. * Gets all reports
  52. *
  53. * @param {object} session - the session object automatically added by socket.io
  54. * @param {Function} cb - gets called with the result
  55. */
  56. index: isAdminRequired(async (session, cb) => {
  57. const reportModel = await DBModule.runJob("GET_MODEL", {
  58. modelName: "report"
  59. });
  60. async.waterfall(
  61. [
  62. next => {
  63. reportModel.find({ resolved: false }).sort({ released: "desc" }).exec(next);
  64. }
  65. ],
  66. async (err, reports) => {
  67. if (err) {
  68. err = await UtilsModule.runJob("GET_ERROR", { error: err });
  69. console.log("ERROR", "REPORTS_INDEX", `Indexing reports failed. "${err}"`);
  70. return cb({ status: "failure", message: err });
  71. }
  72. console.log("SUCCESS", "REPORTS_INDEX", "Indexing reports successful.");
  73. return cb({ status: "success", data: reports });
  74. }
  75. );
  76. }),
  77. /**
  78. * Gets a specific report
  79. *
  80. * @param {object} session - the session object automatically added by socket.io
  81. * @param {string} reportId - the id of the report to return
  82. * @param {Function} cb - gets called with the result
  83. */
  84. findOne: isAdminRequired(async (session, reportId, cb) => {
  85. const reportModel = await DBModule.runJob("GET_MODEL", {
  86. modelName: "report"
  87. });
  88. async.waterfall(
  89. [
  90. next => {
  91. reportModel.findOne({ _id: reportId }).exec(next);
  92. }
  93. ],
  94. async (err, report) => {
  95. if (err) {
  96. err = await UtilsModule.runJob("GET_ERROR", { error: err });
  97. console.log("ERROR", "REPORTS_FIND_ONE", `Finding report "${reportId}" failed. "${err}"`);
  98. return cb({ status: "failure", message: err });
  99. }
  100. console.log("SUCCESS", "REPORTS_FIND_ONE", `Finding report "${reportId}" successful.`);
  101. return cb({ status: "success", data: report });
  102. }
  103. );
  104. }),
  105. /**
  106. * Gets all reports for a songId (_id)
  107. *
  108. * @param {object} session - the session object automatically added by socket.io
  109. * @param {string} songId - the id of the song to index reports for
  110. * @param {Function} cb - gets called with the result
  111. */
  112. getReportsForSong: isAdminRequired(async (session, songId, cb) => {
  113. const reportModel = await DBModule.runJob("GET_MODEL", {
  114. modelName: "report"
  115. });
  116. async.waterfall(
  117. [
  118. next => {
  119. reportModel
  120. .find({ song: { _id: songId }, resolved: false })
  121. .sort({ released: "desc" })
  122. .exec(next);
  123. },
  124. (reports, next) => {
  125. const data = [];
  126. for (let i = 0; i < reports.length; i += 1) {
  127. data.push(reports[i]._id);
  128. }
  129. next(null, data);
  130. }
  131. ],
  132. async (err, data) => {
  133. if (err) {
  134. err = await UtilsModule.runJob("GET_ERROR", { error: err });
  135. console.log(
  136. "ERROR",
  137. "GET_REPORTS_FOR_SONG",
  138. `Indexing reports for song "${songId}" failed. "${err}"`
  139. );
  140. return cb({ status: "failure", message: err });
  141. }
  142. console.log("SUCCESS", "GET_REPORTS_FOR_SONG", `Indexing reports for song "${songId}" successful.`);
  143. return cb({ status: "success", data });
  144. }
  145. );
  146. }),
  147. /**
  148. * Resolves a report
  149. *
  150. * @param {object} session - the session object automatically added by socket.io
  151. * @param {string} reportId - the id of the report that is getting resolved
  152. * @param {Function} cb - gets called with the result
  153. */
  154. resolve: isAdminRequired(async (session, reportId, cb) => {
  155. const reportModel = await DBModule.runJob("GET_MODEL", {
  156. modelName: "report"
  157. });
  158. async.waterfall(
  159. [
  160. next => {
  161. reportModel.findOne({ _id: reportId }).exec(next);
  162. },
  163. (report, next) => {
  164. if (!report) return next("Report not found.");
  165. report.resolved = true;
  166. return report.save(err => {
  167. if (err) return next(err.message);
  168. return next();
  169. });
  170. }
  171. ],
  172. async err => {
  173. if (err) {
  174. err = await UtilsModule.runJob("GET_ERROR", { error: err });
  175. console.log(
  176. "ERROR",
  177. "REPORTS_RESOLVE",
  178. `Resolving report "${reportId}" failed by user "${session.userId}". "${err}"`
  179. );
  180. return cb({ status: "failure", message: err });
  181. }
  182. CacheModule.runJob("PUB", {
  183. channel: "report.resolve",
  184. value: reportId
  185. });
  186. console.log("SUCCESS", "REPORTS_RESOLVE", `User "${session.userId}" resolved report "${reportId}".`);
  187. return cb({
  188. status: "success",
  189. message: "Successfully resolved Report"
  190. });
  191. }
  192. );
  193. }),
  194. /**
  195. * Creates a new report
  196. *
  197. * @param {object} session - the session object automatically added by socket.io
  198. * @param {object} data - the object of the report data
  199. * @param {Function} cb - gets called with the result
  200. */
  201. create: isLoginRequired(async (session, data, cb) => {
  202. const reportModel = await DBModule.runJob("GET_MODEL", {
  203. modelName: "report"
  204. });
  205. const songModel = await DBModule.runJob("GET_MODEL", { modelName: "song" });
  206. async.waterfall(
  207. [
  208. next => {
  209. songModel.findOne({ songId: data.songId }).exec(next);
  210. },
  211. (song, next) => {
  212. if (!song) return next("Song not found.");
  213. return SongsModule.runJob("GET_SONG", { id: song._id })
  214. .then(response => {
  215. next(null, response.song);
  216. })
  217. .catch(next);
  218. },
  219. (song, next) => {
  220. if (!song) return next("Song not found.");
  221. delete data.songId;
  222. data.song = {
  223. _id: song._id,
  224. songId: song.songId
  225. };
  226. for (let z = 0; z < data.issues.length; z += 1) {
  227. if (reportableIssues.filter(issue => issue.name === data.issues[z].name).length > 0) {
  228. for (let r = 0; r < reportableIssues.length; r += 1) {
  229. if (
  230. reportableIssues[r].reasons.every(
  231. reason => data.issues[z].reasons.indexOf(reason) < -1
  232. )
  233. ) {
  234. return cb({
  235. status: "failure",
  236. message: "Invalid data"
  237. });
  238. }
  239. }
  240. } else
  241. return cb({
  242. status: "failure",
  243. message: "Invalid data"
  244. });
  245. }
  246. return next();
  247. },
  248. next => {
  249. const issues = [];
  250. for (let r = 0; r < data.issues.length; r += 1) {
  251. if (!data.issues[r].reasons.length <= 0) issues.push(data.issues[r]);
  252. }
  253. data.issues = issues;
  254. next();
  255. },
  256. next => {
  257. data.createdBy = session.userId;
  258. data.createdAt = Date.now();
  259. reportModel.create(data, next);
  260. }
  261. ],
  262. async (err, report) => {
  263. if (err) {
  264. err = await UtilsModule.runJob("GET_ERROR", { error: err });
  265. console.log(
  266. "ERROR",
  267. "REPORTS_CREATE",
  268. `Creating report for "${data.song._id}" failed by user "${session.userId}". "${err}"`
  269. );
  270. return cb({ status: "failure", message: err });
  271. }
  272. CacheModule.runJob("PUB", {
  273. channel: "report.create",
  274. value: report
  275. });
  276. console.log(
  277. "SUCCESS",
  278. "REPORTS_CREATE",
  279. `User "${session.userId}" created report for "${data.songId}".`
  280. );
  281. return cb({
  282. status: "success",
  283. message: "Successfully created report"
  284. });
  285. }
  286. );
  287. })
  288. };