io.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. /**
  2. * @file
  3. */
  4. import config from "config";
  5. import async from "async";
  6. import WebSocket from "ws";
  7. import { EventEmitter } from "events";
  8. import CoreClass from "../core";
  9. let IOModule;
  10. let AppModule;
  11. let CacheModule;
  12. let UtilsModule;
  13. let DBModule;
  14. let PunishmentsModule;
  15. class _IOModule extends CoreClass {
  16. // eslint-disable-next-line require-jsdoc
  17. constructor() {
  18. super("io");
  19. IOModule = this;
  20. }
  21. /**
  22. * Initialises the io module
  23. *
  24. * @returns {Promise} - returns promise (reject, resolve)
  25. */
  26. async initialize() {
  27. this.setStage(1);
  28. AppModule = this.moduleManager.modules.app;
  29. CacheModule = this.moduleManager.modules.cache;
  30. UtilsModule = this.moduleManager.modules.utils;
  31. DBModule = this.moduleManager.modules.db;
  32. PunishmentsModule = this.moduleManager.modules.punishments;
  33. this.actions = (await import("./actions")).default;
  34. this.userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" });
  35. this.setStage(2);
  36. this.SIDname = config.get("cookie.SIDname");
  37. // TODO: Check every 30s/, for all sockets, if they are still allowed to be in the rooms they are in, and on socket at all (permission changing/banning)
  38. const server = await AppModule.runJob("SERVER");
  39. // this._io.origins(config.get("cors.origin"));
  40. // this._io = socketio(server);
  41. this._io = new WebSocket.Server({ server, path: "/ws" });
  42. return new Promise(resolve => {
  43. this.setStage(3);
  44. // this._io.use(async (socket, cb) => {
  45. // IOModule.runJob("HANDLE_IO_USE", { socket, cb });
  46. // });
  47. this.setStage(4);
  48. this._io.on("connection", async (socket, req) => {
  49. socket.dispatch = (...args) => socket.send(JSON.stringify(args));
  50. console.log(socket.actions);
  51. socket.actions = new EventEmitter();
  52. socket.actions.setMaxListeners(0);
  53. socket.listen = (target, cb) => socket.actions.addListener(target, args => cb(args));
  54. socket.dispatch("test2", { color: "red" }, 9);
  55. IOModule.runJob("HANDLE_IO_USE", { socket, req }).then(socket =>
  56. IOModule.runJob("HANDLE_IO_CONNECTION", { socket })
  57. );
  58. });
  59. this.setStage(5);
  60. return resolve();
  61. });
  62. }
  63. /**
  64. * Returns the socket io variable
  65. *
  66. * @returns {Promise} - returns a promise (resolve, reject)
  67. */
  68. IO() {
  69. return new Promise(resolve => {
  70. resolve(IOModule._io);
  71. });
  72. }
  73. /**
  74. * Returns whether there is a socket for a session id or not
  75. *
  76. * @param {object} payload - object containing the payload
  77. * @param {string} payload.sessionId - user session id
  78. * @returns {Promise} - returns promise (reject, resolve)
  79. */
  80. async SOCKET_FROM_SESSION(payload) {
  81. // socketId
  82. return new Promise((resolve, reject) => {
  83. const ns = IOModule._io.of("/");
  84. if (ns) {
  85. return resolve(ns.connected[payload.socketId]);
  86. }
  87. return reject();
  88. });
  89. }
  90. /**
  91. * Gets all sockets for a specified session id
  92. *
  93. * @param {object} payload - object containing the payload
  94. * @param {string} payload.sessionId - user session id
  95. * @returns {Promise} - returns promise (reject, resolve)
  96. */
  97. async SOCKETS_FROM_SESSION_ID(payload) {
  98. return new Promise(resolve => {
  99. const ns = IOModule._io.of("/");
  100. const sockets = [];
  101. if (ns) {
  102. return async.each(
  103. Object.keys(ns.connected),
  104. (id, next) => {
  105. const { session } = ns.connected[id];
  106. if (session.sessionId === payload.sessionId) sockets.push(session.sessionId);
  107. next();
  108. },
  109. () => {
  110. resolve({ sockets });
  111. }
  112. );
  113. }
  114. return resolve();
  115. });
  116. }
  117. /**
  118. * Returns any sockets for a specific user
  119. *
  120. * @param {object} payload - object that contains the payload
  121. * @param {string} payload.userId - the user id
  122. * @returns {Promise} - returns promise (reject, resolve)
  123. */
  124. async SOCKETS_FROM_USER(payload) {
  125. return new Promise((resolve, reject) => {
  126. const ns = IOModule._io.of("/");
  127. const sockets = [];
  128. if (ns) {
  129. return async.eachLimit(
  130. Object.keys(ns.connected),
  131. 1,
  132. (id, next) => {
  133. const { session } = ns.connected[id];
  134. if (session.sessionId) {
  135. CacheModule.runJob("HGET", { table: "sessions", key: session.sessionId }, this)
  136. .then(session => {
  137. if (session && session.userId === payload.userId) sockets.push(ns.connected[id]);
  138. next();
  139. })
  140. .catch(err => next(err));
  141. } else next();
  142. },
  143. err => {
  144. if (err) return reject(err);
  145. return resolve({ sockets });
  146. }
  147. );
  148. }
  149. return resolve();
  150. });
  151. }
  152. /**
  153. * Returns any sockets from a specific ip address
  154. *
  155. * @param {object} payload - object that contains the payload
  156. * @param {string} payload.ip - the ip address in question
  157. * @returns {Promise} - returns promise (reject, resolve)
  158. */
  159. async SOCKETS_FROM_IP(payload) {
  160. return new Promise(resolve => {
  161. const ns = IOModule._io.of("/");
  162. const sockets = [];
  163. if (ns) {
  164. return async.each(
  165. Object.keys(ns.connected),
  166. (id, next) => {
  167. const { session } = ns.connected[id];
  168. CacheModule.runJob(
  169. "HGET",
  170. {
  171. table: "sessions",
  172. key: session.sessionId
  173. },
  174. this
  175. )
  176. .then(session => {
  177. if (session && ns.connected[id].ip === payload.ip) sockets.push(ns.connected[id]);
  178. next();
  179. })
  180. .catch(() => next());
  181. },
  182. () => {
  183. resolve(sockets);
  184. }
  185. );
  186. }
  187. return resolve();
  188. });
  189. }
  190. /**
  191. * Returns any sockets from a specific user without using redis/cache
  192. *
  193. * @param {object} payload - object that contains the payload
  194. * @param {string} payload.userId - the id of the user in question
  195. * @returns {Promise} - returns promise (reject, resolve)
  196. */
  197. async SOCKETS_FROM_USER_WITHOUT_CACHE(payload) {
  198. return new Promise(resolve => {
  199. const ns = IOModule._io.of("/");
  200. const sockets = [];
  201. if (ns) {
  202. return async.each(
  203. Object.keys(ns.connected),
  204. (id, next) => {
  205. const { session } = ns.connected[id];
  206. if (session.userId === payload.userId) sockets.push(ns.connected[id]);
  207. next();
  208. },
  209. () => {
  210. resolve({ sockets });
  211. }
  212. );
  213. }
  214. return resolve();
  215. });
  216. }
  217. /**
  218. * Allows a socket to leave any rooms they are connected to
  219. *
  220. * @param {object} payload - object that contains the payload
  221. * @param {string} payload.socketId - the id of the socket which should leave all their rooms
  222. * @returns {Promise} - returns promise (reject, resolve)
  223. */
  224. async SOCKET_LEAVE_ROOMS(payload) {
  225. const socket = await IOModule.runJob(
  226. "SOCKET_FROM_SESSION",
  227. {
  228. socketId: payload.socketId
  229. },
  230. this
  231. );
  232. return new Promise(resolve => {
  233. const { rooms } = socket;
  234. Object.keys(rooms).forEach(roomKey => {
  235. const room = rooms[roomKey];
  236. socket.leave(room);
  237. });
  238. return resolve();
  239. });
  240. }
  241. /**
  242. * Allows a socket to join a specified room
  243. *
  244. * @param {object} payload - object that contains the payload
  245. * @param {string} payload.socketId - the id of the socket which should join the room
  246. * @param {object} payload.room - the object representing the room the socket should join
  247. * @returns {Promise} - returns promise (reject, resolve)
  248. */
  249. async SOCKET_JOIN_ROOM(payload) {
  250. const socket = await IOModule.runJob(
  251. "SOCKET_FROM_SESSION",
  252. {
  253. socketId: payload.socketId
  254. },
  255. this
  256. );
  257. return new Promise(resolve => {
  258. const { rooms } = socket;
  259. Object.keys(rooms).forEach(roomKey => {
  260. const room = rooms[roomKey];
  261. socket.leave(room);
  262. });
  263. socket.join(payload.room);
  264. return resolve();
  265. });
  266. }
  267. // UNKNOWN
  268. // eslint-disable-next-line require-jsdoc
  269. async SOCKET_JOIN_SONG_ROOM(payload) {
  270. // socketId, room
  271. const socket = await IOModule.runJob(
  272. "SOCKET_FROM_SESSION",
  273. {
  274. socketId: payload.socketId
  275. },
  276. this
  277. );
  278. return new Promise(resolve => {
  279. const { rooms } = socket;
  280. Object.keys(rooms).forEach(roomKey => {
  281. const room = rooms[roomKey];
  282. if (room.indexOf("song.") !== -1) socket.leave(room);
  283. });
  284. socket.join(payload.room);
  285. return resolve();
  286. });
  287. }
  288. // UNKNOWN
  289. // eslint-disable-next-line require-jsdoc
  290. SOCKETS_JOIN_SONG_ROOM(payload) {
  291. // sockets, room
  292. return new Promise(resolve => {
  293. Object.keys(payload.sockets).forEach(socketKey => {
  294. const socket = payload.sockets[socketKey];
  295. const { rooms } = socket;
  296. Object.keys(rooms).forEach(roomKey => {
  297. const room = rooms[roomKey];
  298. if (room.indexOf("song.") !== -1) socket.leave(room);
  299. });
  300. socket.join(payload.room);
  301. });
  302. return resolve();
  303. });
  304. }
  305. // UNKNOWN
  306. // eslint-disable-next-line require-jsdoc
  307. SOCKETS_LEAVE_SONG_ROOMS(payload) {
  308. // sockets
  309. return new Promise(resolve => {
  310. Object.keys(payload.sockets).forEach(socketKey => {
  311. const socket = payload.sockets[socketKey];
  312. const { rooms } = socket;
  313. Object.keys(rooms).forEach(roomKey => {
  314. const room = rooms[roomKey];
  315. if (room.indexOf("song.") !== -1) socket.leave(room);
  316. });
  317. });
  318. resolve();
  319. });
  320. }
  321. /**
  322. * Emits arguments to any sockets that are in a specified a room
  323. *
  324. * @param {object} payload - object that contains the payload
  325. * @param {string} payload.room - the name of the room to emit arguments
  326. * @param {object} payload.args - any arguments to be emitted to the sockets in the specific room
  327. * @returns {Promise} - returns promise (reject, resolve)
  328. */
  329. async EMIT_TO_ROOM(payload) {
  330. return new Promise(resolve =>
  331. // const { sockets } = IOModule._io.sockets;
  332. // Object.keys(sockets).forEach(socketKey => {
  333. // const socket = sockets[socketKey];
  334. // if (socket.rooms[payload.room]) {
  335. // socket.dispatch(...payload.args);
  336. // }
  337. // });
  338. resolve()
  339. );
  340. }
  341. /**
  342. * Gets any sockets connected to a room
  343. *
  344. * @param {object} payload - object that contains the payload
  345. * @param {string} payload.room - the name of the room
  346. * @returns {Promise} - returns promise (reject, resolve)
  347. */
  348. async GET_ROOM_SOCKETS(payload) {
  349. return new Promise(resolve => {
  350. const { sockets } = IOModule._io.sockets;
  351. const roomSockets = [];
  352. Object.keys(sockets).forEach(socketKey => {
  353. const socket = sockets[socketKey];
  354. if (socket.rooms[payload.room]) roomSockets.push(socket);
  355. });
  356. return resolve(roomSockets);
  357. });
  358. }
  359. /**
  360. * Handles io.use
  361. *
  362. * @param {object} payload - object that contains the payload
  363. * @returns {Promise} - returns promise (reject, resolve)
  364. */
  365. async HANDLE_IO_USE(payload) {
  366. console.log("io use");
  367. return new Promise(resolve => {
  368. const { socket, req } = payload;
  369. let SID;
  370. socket.ip = req.headers["x-forwarded-for"] || "0.0.0.0";
  371. return async.waterfall(
  372. [
  373. next => {
  374. if (!req.headers.cookie) return next("No cookie exists yet.");
  375. return UtilsModule.runJob("PARSE_COOKIES", { cookieString: req.headers.cookie }, this).then(
  376. res => {
  377. SID = res[IOModule.SIDname];
  378. next(null);
  379. }
  380. );
  381. },
  382. next => {
  383. if (!SID) return next("No SID.");
  384. return next();
  385. },
  386. next => {
  387. CacheModule.runJob("HGET", { table: "sessions", key: SID }, this)
  388. .then(session => next(null, session))
  389. .catch(next);
  390. },
  391. (session, next) => {
  392. if (!session) return next("No session found.");
  393. session.refreshDate = Date.now();
  394. socket.session = session;
  395. return CacheModule.runJob(
  396. "HSET",
  397. { table: "sessions", key: SID, value: session },
  398. this
  399. ).then(session => next(null, session));
  400. },
  401. (res, next) => {
  402. // check if a session's user / IP is banned
  403. PunishmentsModule.runJob("GET_PUNISHMENTS", {}, this)
  404. .then(punishments => {
  405. const isLoggedIn = !!(socket.session && socket.session.refreshDate);
  406. const userId = isLoggedIn ? socket.session.userId : null;
  407. const banishment = {
  408. banned: false,
  409. ban: 0
  410. };
  411. punishments.forEach(punishment => {
  412. if (punishment.expiresAt > banishment.ban) banishment.ban = punishment;
  413. if (punishment.type === "banUserId" && isLoggedIn && punishment.value === userId)
  414. banishment.banned = true;
  415. if (punishment.type === "banUserIp" && punishment.value === socket.ip)
  416. banishment.banned = true;
  417. });
  418. socket.banishment = banishment;
  419. next();
  420. })
  421. .catch(() => {
  422. next();
  423. });
  424. }
  425. ],
  426. () => {
  427. if (!socket.session) socket.session = { socketId: req.headers["sec-websocket-key"] };
  428. else socket.session.socketId = req.headers["sec-websocket-key"];
  429. console.log("session", socket.session);
  430. // cb();
  431. resolve(socket);
  432. }
  433. );
  434. });
  435. }
  436. /**
  437. * Handles io.connection
  438. *
  439. * @param {object} payload - object that contains the payload
  440. * @returns {Promise} - returns promise (reject, resolve)
  441. */
  442. async HANDLE_IO_CONNECTION(payload) {
  443. console.log("handle io connection");
  444. return new Promise(resolve => {
  445. const { socket } = payload;
  446. let sessionInfo = "";
  447. if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;
  448. // if session is banned
  449. if (socket.banishment && socket.banishment.banned) {
  450. IOModule.log(
  451. "INFO",
  452. "IO_BANNED_CONNECTION",
  453. `A user tried to connect, but is currently banned. IP: ${socket.ip}.${sessionInfo}`
  454. );
  455. socket.dispatch("keep.event:banned", socket.banishment.ban);
  456. return socket.disconnect(true);
  457. }
  458. IOModule.log("INFO", "IO_CONNECTION", `User connected. IP: ${socket.ip}.${sessionInfo}`);
  459. // catch when the socket has been disconnected
  460. socket.onclose = () => {
  461. if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;
  462. IOModule.log("INFO", "IO_DISCONNECTION", `User disconnected. IP: ${socket.ip}.${sessionInfo}`);
  463. };
  464. // socket.use((data, next) => {
  465. // if (data.length === 0) return next(new Error("Not enough arguments specified."));
  466. // if (typeof data[0] !== "string") return next(new Error("First argument must be a string."));
  467. // const namespaceAction = data[0];
  468. // if (
  469. // !namespaceAction ||
  470. // namespaceAction.indexOf(".") === -1 ||
  471. // namespaceAction.indexOf(".") !== namespaceAction.lastIndexOf(".")
  472. // )
  473. // return next(new Error("Invalid first argument"));
  474. // const namespace = data[0].split(".")[0];
  475. // const action = data[0].split(".")[1];
  476. // if (!namespace) return next(new Error("Invalid namespace."));
  477. // if (!action) return next(new Error("Invalid action."));
  478. // if (!IOModule.actions[namespace]) return next(new Error("Namespace not found."));
  479. // if (!IOModule.actions[namespace][action]) return next(new Error("Action not found."));
  480. // return next();
  481. // });
  482. // catch errors on the socket (internal to socket.io)
  483. socket.onerror = console.error;
  484. if (socket.session.sessionId) {
  485. CacheModule.runJob("HGET", {
  486. table: "sessions",
  487. key: socket.session.sessionId
  488. })
  489. .then(session => {
  490. if (session && session.userId) {
  491. IOModule.userModel.findOne({ _id: session.userId }, (err, user) => {
  492. if (err || !user) return socket.dispatch("ready", false);
  493. let role = "";
  494. let username = "";
  495. let userId = "";
  496. if (user) {
  497. role = user.role;
  498. username = user.username;
  499. userId = session.userId;
  500. }
  501. return socket.dispatch("ready", true, role, username, userId);
  502. });
  503. } else socket.dispatch("ready", false);
  504. })
  505. .catch(() => {
  506. socket.dispatch("ready", false);
  507. });
  508. } else socket.dispatch("ready", false);
  509. // have the socket listen for each action
  510. Object.keys(IOModule.actions).forEach(namespace => {
  511. Object.keys(IOModule.actions[namespace]).forEach(action => {
  512. // the full name of the action
  513. const name = `${namespace}.${action}`;
  514. socket.onmessage = message => {
  515. const data = JSON.parse(message.data);
  516. if (data[data.length - 1].callbackRef) {
  517. const { callbackRef } = data[data.length - 1];
  518. data.pop();
  519. return socket.actions.emit(data.shift(0), [
  520. ...data,
  521. res => socket.dispatch("callbackRef", callbackRef, res)
  522. ]);
  523. }
  524. return socket.actions.emit(data.shift(0), data);
  525. };
  526. // listen for this action to be called
  527. socket.listen(name, async args => {
  528. IOModule.runJob("RUN_ACTION", { socket, namespace, action, args });
  529. console.log(name, args);
  530. /* let cb = args[args.length - 1];
  531. if (typeof cb !== "function")
  532. cb = () => {
  533. IOModule.log("INFO", "IO_MODULE", `There was no callback provided for ${name}.`);
  534. };
  535. else args.pop();
  536. if (this.getStatus() !== "READY") {
  537. IOModule.log(
  538. "INFO",
  539. "IO_REJECTED_ACTION",
  540. `A user tried to execute an action, but the IO module is currently not ready. Action: ${namespace}.${action}.`
  541. );
  542. return;
  543. }
  544. IOModule.log("INFO", "IO_ACTION", `A user executed an action. Action: ${namespace}.${action}.`);
  545. let failedGettingSession = false;
  546. // load the session from the cache
  547. if (socket.session.sessionId)
  548. await CacheModule.runJob("HGET", {
  549. table: "sessions",
  550. key: socket.session.sessionId
  551. })
  552. .then(session => {
  553. // make sure the sockets sessionId isn't set if there is no session
  554. if (socket.session.sessionId && session === null) delete socket.session.sessionId;
  555. })
  556. .catch(() => {
  557. failedGettingSession = true;
  558. if (typeof cb === "function")
  559. cb({
  560. status: "error",
  561. message: "An error occurred while obtaining your session"
  562. });
  563. });
  564. if (!failedGettingSession)
  565. try {
  566. // call the action, passing it the session, and the arguments socket.io passed us
  567. this.runJob("RUN_ACTION", { namespace, action, session: socket.session, args })
  568. .then(response => {
  569. if (typeof cb === "function") cb(response);
  570. })
  571. .catch(err => {
  572. if (typeof cb === "function") cb(err);
  573. });
  574. // actions[namespace][action].apply(
  575. // null,
  576. // [socket.session].concat(args).concat([
  577. // result => {
  578. // IOModule.log(
  579. // "INFO",
  580. // "IO_ACTION",
  581. // `Response to action. Action: ${namespace}.${action}. Response status: ${result.status}`
  582. // );
  583. // // respond to the socket with our message
  584. // if (typeof cb === "function") cb(result);
  585. // }
  586. // ])
  587. // );
  588. } catch (err) {
  589. if (typeof cb === "function")
  590. cb({
  591. status: "error",
  592. message: "An error occurred while executing the specified action."
  593. });
  594. IOModule.log(
  595. "ERROR",
  596. "IO_ACTION_ERROR",
  597. `Some type of exception occurred in the action ${namespace}.${action}. Error message: ${err.message}`
  598. );
  599. } */
  600. });
  601. });
  602. });
  603. return resolve();
  604. });
  605. }
  606. /**
  607. * Runs an action
  608. *
  609. * @param {object} payload - object that contains the payload
  610. * @returns {Promise} - returns promise (reject, resolve)
  611. */
  612. async RUN_ACTION(payload) {
  613. return new Promise((resolve, reject) => {
  614. const { socket, namespace, action, args } = payload;
  615. // the full name of the action
  616. const name = `${namespace}.${action}`;
  617. let cb = args[args.length - 1];
  618. if (typeof cb !== "function")
  619. cb = () => {
  620. IOModule.log("INFO", "IO_MODULE", `There was no callback provided for ${name}.`);
  621. };
  622. else args.pop();
  623. IOModule.log("INFO", "IO_ACTION", `A user executed an action. Action: ${namespace}.${action}.`);
  624. // load the session from the cache
  625. new Promise(resolve => {
  626. if (socket.session.sessionId)
  627. CacheModule.runJob("HGET", {
  628. table: "sessions",
  629. key: socket.session.sessionId
  630. })
  631. .then(session => {
  632. // make sure the sockets sessionId isn't set if there is no session
  633. if (socket.session.sessionId && session === null) delete socket.session.sessionId;
  634. resolve();
  635. })
  636. .catch(() => {
  637. if (typeof cb === "function")
  638. cb({
  639. status: "error",
  640. message: "An error occurred while obtaining your session"
  641. });
  642. reject(new Error("An error occurred while obtaining the session"));
  643. });
  644. else resolve();
  645. })
  646. .then(() => {
  647. // call the job that calls the action, passing it the session, and the arguments socket.io passed us
  648. IOModule.runJob("RUN_ACTION2", { session: socket.session, namespace, action, args }, this)
  649. .then(response => {
  650. cb(response);
  651. resolve();
  652. })
  653. .catch(err => {
  654. if (typeof cb === "function")
  655. cb({
  656. status: "error",
  657. message: "An error occurred while executing the specified action."
  658. });
  659. reject(err);
  660. IOModule.log(
  661. "ERROR",
  662. "IO_ACTION_ERROR",
  663. `Some type of exception occurred in the action ${namespace}.${action}. Error message: ${err.message}`
  664. );
  665. });
  666. })
  667. .catch(reject);
  668. });
  669. }
  670. /**
  671. * Runs an action
  672. *
  673. * @param {object} payload - object that contains the payload
  674. * @returns {Promise} - returns promise (reject, resolve)
  675. */
  676. async RUN_ACTION2(payload) {
  677. return new Promise((resolve, reject) => {
  678. const { session, namespace, action, args } = payload;
  679. try {
  680. // call the the action, passing it the session, and the arguments socket.io passed us
  681. IOModule.actions[namespace][action].apply(
  682. this,
  683. [session].concat(args).concat([
  684. result => {
  685. IOModule.log(
  686. "INFO",
  687. "RUN_ACTION2",
  688. `Response to action. Action: ${namespace}.${action}. Response status: ${result.status}`
  689. );
  690. resolve(result);
  691. }
  692. ])
  693. );
  694. } catch (err) {
  695. reject(err);
  696. IOModule.log(
  697. "ERROR",
  698. "IO_ACTION_ERROR",
  699. `Some type of exception occurred in the action ${namespace}.${action}. Error message: ${err.message}`
  700. );
  701. }
  702. });
  703. }
  704. }
  705. export default new _IOModule();