api.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import config from "config";
  2. import async from "async";
  3. import crypto from "crypto";
  4. import CoreClass from "../core";
  5. import { hasPermission } from "./hooks/hasPermission";
  6. let AppModule;
  7. let DBModule;
  8. let PlaylistsModule;
  9. let UtilsModule;
  10. let PunishmentsModule;
  11. let CacheModule;
  12. let NotificationsModule;
  13. class _APIModule extends CoreClass {
  14. // eslint-disable-next-line require-jsdoc
  15. constructor() {
  16. super("api");
  17. }
  18. /**
  19. * Initialises the api module
  20. *
  21. * @returns {Promise} - returns promise (reject, resolve)
  22. */
  23. initialize() {
  24. return new Promise((resolve, reject) => {
  25. AppModule = this.moduleManager.modules.app;
  26. DBModule = this.moduleManager.modules.db;
  27. PlaylistsModule = this.moduleManager.modules.playlists;
  28. UtilsModule = this.moduleManager.modules.utils;
  29. PunishmentsModule = this.moduleManager.modules.punishments;
  30. CacheModule = this.moduleManager.modules.cache;
  31. NotificationsModule = this.moduleManager.modules.notifications;
  32. const SIDname = config.get("cookie.SIDname");
  33. const isLoggedIn = (req, res, next) => {
  34. let SID;
  35. async.waterfall(
  36. [
  37. next => {
  38. UtilsModule.runJob("PARSE_COOKIES", {
  39. cookieString: req.headers.cookie
  40. })
  41. .then(res => {
  42. SID = res[SIDname];
  43. next(null);
  44. })
  45. .catch(next);
  46. },
  47. next => {
  48. if (!SID) return next("No SID.");
  49. return next();
  50. },
  51. next => {
  52. CacheModule.runJob("HGET", { table: "sessions", key: SID }).then(session =>
  53. next(null, session)
  54. );
  55. },
  56. (session, next) => {
  57. if (!session) return next("No session found.");
  58. session.refreshDate = Date.now();
  59. req.session = session;
  60. return CacheModule.runJob("HSET", {
  61. table: "sessions",
  62. key: SID,
  63. value: session
  64. }).then(session => {
  65. next(null, session);
  66. });
  67. },
  68. (res, next) => {
  69. // check if a session's user / IP is banned
  70. PunishmentsModule.runJob("GET_PUNISHMENTS", {})
  71. .then(punishments => {
  72. const isLoggedIn = !!(req.session && req.session.refreshDate);
  73. const userId = isLoggedIn ? req.session.userId : null;
  74. const banishment = { banned: false, ban: 0 };
  75. punishments.forEach(punishment => {
  76. if (punishment.expiresAt > banishment.ban) banishment.ban = punishment;
  77. if (
  78. punishment.type === "banUserId" &&
  79. isLoggedIn &&
  80. punishment.value === userId
  81. )
  82. banishment.banned = true;
  83. if (punishment.type === "banUserIp" && punishment.value === req.ip)
  84. banishment.banned = true;
  85. });
  86. req.banishment = banishment;
  87. next();
  88. })
  89. .catch(() => {
  90. next();
  91. });
  92. }
  93. ],
  94. err => {
  95. if (err) return res.json({ status: "error", message: "You are not logged in" });
  96. return next();
  97. }
  98. );
  99. };
  100. AppModule.runJob("GET_APP", {})
  101. .then(response => {
  102. response.app.get("/", (req, res) => {
  103. res.json({
  104. status: "success",
  105. message: "Coming Soon"
  106. });
  107. });
  108. response.app.get("/export/playlist/:playlistId", async (req, res) => {
  109. const { playlistId } = req.params;
  110. PlaylistsModule.runJob("GET_PLAYLIST", { playlistId })
  111. .then(playlist => {
  112. if (!playlist) res.json({ status: "error", message: "Playlist not found." });
  113. else if (playlist.privacy === "public") res.json({ status: "success", playlist });
  114. else
  115. isLoggedIn(req, res, () => {
  116. if (playlist.createdBy === req.session.userId)
  117. res.json({ status: "success", playlist });
  118. else
  119. hasPermission("playlists.get", req.session.userId)
  120. .then(() => res.json({ status: "success", playlist }))
  121. .catch(() =>
  122. res.json({
  123. status: "error",
  124. message: "You're not allowed to download this playlist."
  125. })
  126. );
  127. });
  128. })
  129. .catch(err => {
  130. res.json({ status: "error", message: err.message });
  131. });
  132. });
  133. if (config.get("debug.stationIssue")) {
  134. response.app.get("/debug_station", async (req, res) => {
  135. const responseObject = {};
  136. const stationModel = await DBModule.runJob("GET_MODEL", {
  137. modelName: "station"
  138. });
  139. async.waterfall(
  140. [
  141. next => {
  142. stationModel.find({}, next);
  143. },
  144. (stations, next) => {
  145. responseObject.mongo = {
  146. stations
  147. };
  148. next();
  149. },
  150. next => {
  151. CacheModule.runJob("HGETALL", { table: "stations" })
  152. .then(stations => {
  153. next(null, stations);
  154. })
  155. .catch(err => {
  156. console.log(err);
  157. next(err);
  158. });
  159. },
  160. (stations, next) => {
  161. responseObject.redis = {
  162. stations
  163. };
  164. next();
  165. },
  166. next => {
  167. responseObject.cryptoExamples = {};
  168. responseObject.mongo.stations.forEach(station => {
  169. const payloadName = `stations.nextSong?id=${station._id}`;
  170. responseObject.cryptoExamples[station._id] = crypto
  171. .createHash("md5")
  172. .update(`_notification:${payloadName}_`)
  173. .digest("hex");
  174. });
  175. next();
  176. },
  177. next => {
  178. NotificationsModule.pub
  179. .KEYS("*")
  180. .then(redisKeys => next(null, redisKeys))
  181. .catch(next);
  182. },
  183. (redisKeys, next) => {
  184. responseObject.redis = {
  185. ...redisKeys,
  186. ttl: {}
  187. };
  188. async.eachLimit(
  189. redisKeys,
  190. 1,
  191. (redisKey, next) => {
  192. NotificationsModule.pub
  193. .TTL(redisKey)
  194. .then(ttl => {
  195. responseObject.redis.ttl[redisKey] = ttl;
  196. next();
  197. })
  198. .catch(next);
  199. },
  200. next
  201. );
  202. },
  203. next => {
  204. responseObject.debugLogs = this.moduleManager.debugLogs.stationIssue;
  205. next();
  206. },
  207. next => {
  208. responseObject.debugJobs = this.moduleManager.debugJobs;
  209. next();
  210. }
  211. ],
  212. err => {
  213. if (err) {
  214. console.log(err);
  215. return res.json({
  216. error: err,
  217. objectSoFar: responseObject
  218. });
  219. }
  220. const responseJson = JSON.stringify(responseObject, (key, value) => {
  221. if (
  222. key === "module" ||
  223. key === "task" ||
  224. key === "onFinish" ||
  225. key === "server" ||
  226. key === "nsp" ||
  227. key === "socket" ||
  228. key === "res" ||
  229. key === "client" ||
  230. key === "_idleNext" ||
  231. key === "_idlePrev"
  232. ) {
  233. return undefined;
  234. }
  235. if (key === "parentJob" && value) return value.toString();
  236. return value;
  237. });
  238. return res.end(responseJson);
  239. }
  240. );
  241. });
  242. }
  243. resolve();
  244. })
  245. .catch(err => {
  246. reject(err);
  247. });
  248. });
  249. }
  250. }
  251. export default new _APIModule();