punishments.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. import async from "async";
  2. import { isAdminRequired } from "./hooks";
  3. // eslint-disable-next-line
  4. import moduleManager from "../../index";
  5. const DBModule = moduleManager.modules.db;
  6. const UtilsModule = moduleManager.modules.utils;
  7. const WSModule = moduleManager.modules.ws;
  8. const CacheModule = moduleManager.modules.cache;
  9. const PunishmentsModule = moduleManager.modules.punishments;
  10. CacheModule.runJob("SUB", {
  11. channel: "ip.ban",
  12. cb: data => {
  13. WSModule.runJob("EMIT_TO_ROOM", {
  14. room: "admin.punishments",
  15. args: ["event:admin.punishment.created", { data: { punishment: data.punishment } }]
  16. });
  17. WSModule.runJob("SOCKETS_FROM_IP", { ip: data.ip }, this).then(sockets => {
  18. sockets.forEach(socket => {
  19. socket.close();
  20. });
  21. });
  22. }
  23. });
  24. export default {
  25. /**
  26. * Gets punishments, used in the admin punishments page by the AdvancedTable component
  27. *
  28. * @param {object} session - the session object automatically added by the websocket
  29. * @param page - the page
  30. * @param pageSize - the size per page
  31. * @param properties - the properties to return for each punishment
  32. * @param sort - the sort object
  33. * @param queries - the queries array
  34. * @param operator - the operator for queries
  35. * @param cb
  36. */
  37. getData: isAdminRequired(async function getSet(session, page, pageSize, properties, sort, queries, operator, cb) {
  38. async.waterfall(
  39. [
  40. next => {
  41. DBModule.runJob(
  42. "GET_DATA",
  43. {
  44. page,
  45. pageSize,
  46. properties,
  47. sort,
  48. queries,
  49. operator,
  50. modelName: "punishment",
  51. blacklistedProperties: [],
  52. specialProperties: {
  53. status: [
  54. {
  55. $addFields: {
  56. status: {
  57. $cond: [
  58. { $eq: ["$active", true] },
  59. {
  60. $cond: [
  61. { $gt: [new Date(), "$expiresAt"] },
  62. "Inactive",
  63. "Active"
  64. ]
  65. },
  66. "Inactive"
  67. ]
  68. }
  69. }
  70. }
  71. ],
  72. value: [
  73. {
  74. $addFields: {
  75. valueOID: {
  76. $convert: {
  77. input: "$value",
  78. to: "objectId",
  79. onError: "unknown",
  80. onNull: "unknown"
  81. }
  82. }
  83. }
  84. },
  85. {
  86. $lookup: {
  87. from: "users",
  88. localField: "valueOID",
  89. foreignField: "_id",
  90. as: "valueUser"
  91. }
  92. },
  93. {
  94. $unwind: {
  95. path: "$valueUser",
  96. preserveNullAndEmptyArrays: true
  97. }
  98. },
  99. {
  100. $addFields: {
  101. valueUsername: {
  102. $cond: [
  103. { $eq: ["$type", "banUserId"] },
  104. { $ifNull: ["$valueUser.username", "unknown"] },
  105. null
  106. ]
  107. }
  108. }
  109. },
  110. {
  111. $project: {
  112. valueOID: 0,
  113. valueUser: 0
  114. }
  115. }
  116. ],
  117. punishedBy: [
  118. {
  119. $addFields: {
  120. punishedByOID: {
  121. $convert: {
  122. input: "$punishedBy",
  123. to: "objectId",
  124. onError: "unknown",
  125. onNull: "unknown"
  126. }
  127. }
  128. }
  129. },
  130. {
  131. $lookup: {
  132. from: "users",
  133. localField: "punishedByOID",
  134. foreignField: "_id",
  135. as: "punishedByUser"
  136. }
  137. },
  138. {
  139. $unwind: {
  140. path: "$punishedByUser",
  141. preserveNullAndEmptyArrays: true
  142. }
  143. },
  144. {
  145. $addFields: {
  146. punishedByUsername: {
  147. $ifNull: ["$punishedByUser.username", "unknown"]
  148. }
  149. }
  150. },
  151. {
  152. $project: {
  153. punishedByOID: 0,
  154. punishedByUser: 0
  155. }
  156. }
  157. ]
  158. },
  159. specialQueries: {
  160. value: newQuery => ({ $or: [newQuery, { valueUsername: newQuery.value }] }),
  161. punishedBy: newQuery => ({
  162. $or: [newQuery, { punishedByUsername: newQuery.punishedBy }]
  163. })
  164. }
  165. },
  166. this
  167. )
  168. .then(response => {
  169. next(null, response);
  170. })
  171. .catch(err => {
  172. next(err);
  173. });
  174. }
  175. ],
  176. async (err, response) => {
  177. if (err && err !== true) {
  178. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  179. this.log("ERROR", "PUNISHMENTS_GET_DATA", `Failed to get data from punishments. "${err}"`);
  180. return cb({ status: "error", message: err });
  181. }
  182. this.log("SUCCESS", "PUNISHMENTS_GET_DATA", `Got data from punishments successfully.`);
  183. return cb({
  184. status: "success",
  185. message: "Successfully got data from punishments.",
  186. data: response
  187. });
  188. }
  189. );
  190. }),
  191. /**
  192. * Gets all punishments for a user
  193. *
  194. * @param {object} session - the session object automatically added by the websocket
  195. * @param {string} userId - the id of the user
  196. * @param {Function} cb - gets called with the result
  197. */
  198. getPunishmentsForUser: isAdminRequired(async function getPunishmentsForUser(session, userId, cb) {
  199. const punishmentModel = await DBModule.runJob("GET_MODEL", { modelName: "punishment" }, this);
  200. punishmentModel.find({ type: "banUserId", value: userId }, async (err, punishments) => {
  201. if (err) {
  202. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  203. this.log(
  204. "ERROR",
  205. "GET_PUNISHMENTS_FOR_USER",
  206. `Getting punishments for user ${userId} failed. "${err}"`
  207. );
  208. return cb({ status: "error", message: err });
  209. }
  210. this.log("SUCCESS", "GET_PUNISHMENTS_FOR_USER", `Got punishments for user ${userId} successful.`);
  211. return cb({ status: "success", data: { punishments } });
  212. });
  213. }),
  214. /**
  215. * Returns a punishment by id
  216. *
  217. * @param {object} session - the session object automatically added by the websocket
  218. * @param {string} punishmentId - the punishment id
  219. * @param {Function} cb - gets called with the result
  220. */
  221. findOne: isAdminRequired(async function findOne(session, punishmentId, cb) {
  222. const punishmentModel = await DBModule.runJob("GET_MODEL", { modelName: "punishment" }, this);
  223. async.waterfall([next => punishmentModel.findOne({ _id: punishmentId }, next)], async (err, punishment) => {
  224. if (err) {
  225. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  226. this.log(
  227. "ERROR",
  228. "GET_PUNISHMENT_BY_ID",
  229. `Getting punishment with id ${punishmentId} failed. "${err}"`
  230. );
  231. return cb({ status: "error", message: err });
  232. }
  233. this.log("SUCCESS", "GET_PUNISHMENT_BY_ID", `Got punishment with id ${punishmentId} successful.`);
  234. return cb({ status: "success", data: { punishment } });
  235. });
  236. }),
  237. /**
  238. * Bans an IP address
  239. *
  240. * @param {object} session - the session object automatically added by the websocket
  241. * @param {string} value - the ip address that is going to be banned
  242. * @param {string} reason - the reason for the ban
  243. * @param {string} expiresAt - the time the ban expires
  244. * @param {Function} cb - gets called with the result
  245. */
  246. banIP: isAdminRequired(function banIP(session, value, reason, expiresAt, cb) {
  247. async.waterfall(
  248. [
  249. next => {
  250. if (!value) return next("You must provide an IP address to ban.");
  251. if (!reason) return next("You must provide a reason for the ban.");
  252. return next();
  253. },
  254. next => {
  255. if (!expiresAt || typeof expiresAt !== "string") return next("Invalid expire date.");
  256. const date = new Date();
  257. switch (expiresAt) {
  258. case "1h":
  259. expiresAt = date.setHours(date.getHours() + 1);
  260. break;
  261. case "12h":
  262. expiresAt = date.setHours(date.getHours() + 12);
  263. break;
  264. case "1d":
  265. expiresAt = date.setDate(date.getDate() + 1);
  266. break;
  267. case "1w":
  268. expiresAt = date.setDate(date.getDate() + 7);
  269. break;
  270. case "1m":
  271. expiresAt = date.setMonth(date.getMonth() + 1);
  272. break;
  273. case "3m":
  274. expiresAt = date.setMonth(date.getMonth() + 3);
  275. break;
  276. case "6m":
  277. expiresAt = date.setMonth(date.getMonth() + 6);
  278. break;
  279. case "1y":
  280. expiresAt = date.setFullYear(date.getFullYear() + 1);
  281. break;
  282. case "never":
  283. expiresAt = new Date(3093527980800000);
  284. break;
  285. default:
  286. return next("Invalid expire date.");
  287. }
  288. return next();
  289. },
  290. next => {
  291. PunishmentsModule.runJob(
  292. "ADD_PUNISHMENT",
  293. {
  294. type: "banUserIp",
  295. value,
  296. reason,
  297. expiresAt,
  298. punishedBy: session.userId
  299. },
  300. this
  301. )
  302. .then(punishment => {
  303. next(null, punishment);
  304. })
  305. .catch(next);
  306. }
  307. ],
  308. async (err, punishment) => {
  309. if (err && err !== true) {
  310. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  311. this.log(
  312. "ERROR",
  313. "BAN_IP",
  314. `User ${session.userId} failed to ban IP address ${value} with the reason ${reason}. '${err}'`
  315. );
  316. cb({ status: "error", message: err });
  317. }
  318. this.log(
  319. "SUCCESS",
  320. "BAN_IP",
  321. `User ${session.userId} has successfully banned IP address ${value} with the reason ${reason}.`
  322. );
  323. CacheModule.runJob("PUB", {
  324. channel: "ip.ban",
  325. value: { ip: value, punishment }
  326. });
  327. return cb({
  328. status: "success",
  329. message: "Successfully banned IP address."
  330. });
  331. }
  332. );
  333. }),
  334. /**
  335. * Deactivates a punishment
  336. *
  337. * @param {object} session - the session object automatically added by the websocket
  338. * @param {string} punishmentId - the MongoDB id of the punishment
  339. * @param {Function} cb - gets called with the result
  340. */
  341. deactivatePunishment: isAdminRequired(function deactivatePunishment(session, punishmentId, cb) {
  342. async.waterfall(
  343. [
  344. next => {
  345. PunishmentsModule.runJob("DEACTIVATE_PUNISHMENT", { punishmentId }, this)
  346. .then(punishment => next(null, punishment._doc))
  347. .catch(next);
  348. }
  349. ],
  350. async (err, punishment) => {
  351. if (err) {
  352. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  353. this.log(
  354. "ERROR",
  355. "DEACTIVATE_PUNISHMENT",
  356. `Deactivating punishment ${punishmentId} failed. "${err}"`
  357. );
  358. return cb({ status: "error", message: err });
  359. }
  360. this.log("SUCCESS", "DEACTIVATE_PUNISHMENT", `Deactivated punishment ${punishmentId} successful.`);
  361. WSModule.runJob("EMIT_TO_ROOM", {
  362. room: `admin.punishments`,
  363. args: [
  364. "event:admin.punishment.updated",
  365. {
  366. data: {
  367. punishment: { ...punishment, status: "Inactive" }
  368. }
  369. }
  370. ]
  371. });
  372. return cb({ status: "success" });
  373. }
  374. );
  375. })
  376. };